@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.
Files changed (120) hide show
  1. package/README.md +12 -5
  2. package/dist/{chunk-BKJB527E.mjs → chunk-3QETHO6P.mjs} +1 -3
  3. package/dist/{chunk-PJLEJVAU.mjs → chunk-42LTUHMZ.mjs} +1 -3
  4. package/dist/{chunk-NY7U7OJW.mjs → chunk-7V7O3Q7Y.mjs} +0 -2
  5. package/dist/{chunk-BXJLBR4G.mjs → chunk-C6M34IVL.mjs} +2 -4
  6. package/dist/{chunk-XZ7CHO2S.mjs → chunk-FSOGMSJH.mjs} +2 -4
  7. package/dist/{chunk-X2MHIWXO.mjs → chunk-IFXZQW4C.mjs} +0 -2
  8. package/dist/{chunk-7ULB6DW4.mjs → chunk-JQ4H4GJ5.mjs} +3 -5
  9. package/dist/{chunk-N5A2TMF6.mjs → chunk-LKSSME2J.mjs} +2 -4
  10. package/dist/{chunk-PLCA4ZDJ.mjs → chunk-LUKR7YKV.mjs} +54 -30
  11. package/dist/{chunk-MXZVF5HR.mjs → chunk-NAKCAL2M.mjs} +0 -2
  12. package/dist/chunk-QMXBY3UI.mjs +1 -0
  13. package/dist/{chunk-MAQ47Q52.mjs → chunk-TACW7Z4D.mjs} +0 -2
  14. package/dist/{chunk-2BOUGCJ7.mjs → chunk-WPZOLGVB.mjs} +4 -6
  15. package/dist/{chunk-66BAUK56.mjs → chunk-X7BHR7WS.mjs} +2 -4
  16. package/dist/{chunk-E5IAZ7E6.mjs → chunk-Z5GXDHGL.mjs} +9 -5
  17. package/dist/{config-OlnzyEUE.d.ts → config-GVKK8IfY.d.ts} +6 -1
  18. package/dist/index.d.ts +4 -4
  19. package/dist/index.mjs +20 -31
  20. package/dist/xchains/bitcoin/index.mjs +14 -16
  21. package/dist/xchains/evm/index.d.ts +1 -1
  22. package/dist/xchains/evm/index.mjs +3 -5
  23. package/dist/xchains/icon/index.mjs +5 -7
  24. package/dist/xchains/injective/index.mjs +3 -5
  25. package/dist/xchains/near/index.mjs +4 -6
  26. package/dist/xchains/solana/index.mjs +5 -7
  27. package/dist/xchains/stacks/index.mjs +3 -5
  28. package/dist/xchains/stellar/index.mjs +4 -6
  29. package/dist/xchains/sui/index.mjs +5 -7
  30. package/docs/ADDING_A_NEW_CHAIN.md +1 -1
  31. package/docs/SUB_PATH_EXPORTS.md +14 -42
  32. package/package.json +32 -24
  33. package/ai-exported/AGENTS.md +0 -122
  34. package/ai-exported/integration/README.md +0 -102
  35. package/ai-exported/integration/ai-rules.md +0 -136
  36. package/ai-exported/integration/architecture.md +0 -181
  37. package/ai-exported/integration/examples/01-minimal-evm.tsx +0 -75
  38. package/ai-exported/integration/examples/02-multi-chain-modal.tsx +0 -169
  39. package/ai-exported/integration/examples/03-nextjs-app-router.tsx +0 -99
  40. package/ai-exported/integration/examples/04-walletconnect-setup.tsx +0 -89
  41. package/ai-exported/integration/examples/README.md +0 -29
  42. package/ai-exported/integration/recipes/batch-operations.md +0 -223
  43. package/ai-exported/integration/recipes/bridge-to-sdk.md +0 -164
  44. package/ai-exported/integration/recipes/chain-detection.md +0 -254
  45. package/ai-exported/integration/recipes/connect-button.md +0 -156
  46. package/ai-exported/integration/recipes/multi-chain-modal.md +0 -199
  47. package/ai-exported/integration/recipes/setup.md +0 -160
  48. package/ai-exported/integration/recipes/sign-message.md +0 -137
  49. package/ai-exported/integration/recipes/sub-path-imports.md +0 -95
  50. package/ai-exported/integration/recipes/switch-chain.md +0 -141
  51. package/ai-exported/integration/recipes/walletconnect-setup.md +0 -139
  52. package/ai-exported/integration/reference/api-surface.md +0 -175
  53. package/ai-exported/integration/reference/chain-support.md +0 -78
  54. package/ai-exported/integration/reference/connectors.md +0 -74
  55. package/ai-exported/integration/reference/hooks.md +0 -204
  56. package/ai-exported/integration/reference/wallet-brands.md +0 -106
  57. package/ai-exported/migration/README.md +0 -49
  58. package/ai-exported/migration/ai-rules.md +0 -144
  59. package/ai-exported/migration/breaking-changes.md +0 -305
  60. package/ai-exported/migration/checklist.md +0 -159
  61. package/ai-exported/migration/recipes/connect-button.md +0 -166
  62. package/ai-exported/migration/recipes/multi-chain-modal.md +0 -244
  63. package/ai-exported/migration/recipes/ssr-setup.md +0 -164
  64. package/ai-exported/migration/recipes/walletconnect-migration.md +0 -168
  65. package/ai-exported/migration/reference/components.md +0 -73
  66. package/ai-exported/migration/reference/config.md +0 -325
  67. package/ai-exported/migration/reference/hooks.md +0 -323
  68. package/ai-exported/migration/reference/imports.md +0 -157
  69. package/dist/chunk-2BOUGCJ7.mjs.map +0 -1
  70. package/dist/chunk-66BAUK56.mjs.map +0 -1
  71. package/dist/chunk-7ULB6DW4.mjs.map +0 -1
  72. package/dist/chunk-BKJB527E.mjs.map +0 -1
  73. package/dist/chunk-BXJLBR4G.mjs.map +0 -1
  74. package/dist/chunk-E5IAZ7E6.mjs.map +0 -1
  75. package/dist/chunk-MAQ47Q52.mjs.map +0 -1
  76. package/dist/chunk-MXZVF5HR.mjs.map +0 -1
  77. package/dist/chunk-N5A2TMF6.mjs.map +0 -1
  78. package/dist/chunk-NY7U7OJW.mjs.map +0 -1
  79. package/dist/chunk-PJLEJVAU.mjs.map +0 -1
  80. package/dist/chunk-PLCA4ZDJ.mjs.map +0 -1
  81. package/dist/chunk-TZMKDXFA.mjs +0 -3
  82. package/dist/chunk-TZMKDXFA.mjs.map +0 -1
  83. package/dist/chunk-X2MHIWXO.mjs.map +0 -1
  84. package/dist/chunk-XZ7CHO2S.mjs.map +0 -1
  85. package/dist/index.cjs +0 -3337
  86. package/dist/index.cjs.map +0 -1
  87. package/dist/index.mjs.map +0 -1
  88. package/dist/xchains/bitcoin/index.cjs +0 -1927
  89. package/dist/xchains/bitcoin/index.cjs.map +0 -1
  90. package/dist/xchains/bitcoin/index.mjs.map +0 -1
  91. package/dist/xchains/evm/index.cjs +0 -316
  92. package/dist/xchains/evm/index.cjs.map +0 -1
  93. package/dist/xchains/evm/index.mjs.map +0 -1
  94. package/dist/xchains/icon/index.cjs +0 -311
  95. package/dist/xchains/icon/index.cjs.map +0 -1
  96. package/dist/xchains/icon/index.mjs.map +0 -1
  97. package/dist/xchains/injective/index.cjs +0 -223
  98. package/dist/xchains/injective/index.cjs.map +0 -1
  99. package/dist/xchains/injective/index.mjs.map +0 -1
  100. package/dist/xchains/near/index.cjs +0 -190
  101. package/dist/xchains/near/index.cjs.map +0 -1
  102. package/dist/xchains/near/index.mjs.map +0 -1
  103. package/dist/xchains/solana/index.cjs +0 -186
  104. package/dist/xchains/solana/index.cjs.map +0 -1
  105. package/dist/xchains/solana/index.mjs.map +0 -1
  106. package/dist/xchains/stacks/index.cjs +0 -240
  107. package/dist/xchains/stacks/index.cjs.map +0 -1
  108. package/dist/xchains/stacks/index.mjs.map +0 -1
  109. package/dist/xchains/stellar/index.cjs +0 -322
  110. package/dist/xchains/stellar/index.cjs.map +0 -1
  111. package/dist/xchains/stellar/index.mjs.map +0 -1
  112. package/dist/xchains/sui/index.cjs +0 -248
  113. package/dist/xchains/sui/index.cjs.map +0 -1
  114. package/dist/xchains/sui/index.mjs.map +0 -1
  115. package/skills/SKILLS.md +0 -84
  116. package/skills/bridge-to-sdk.md +0 -148
  117. package/skills/connect-button.md +0 -116
  118. package/skills/evm-only-walletconnect.md +0 -111
  119. package/skills/multi-chain-modal.md +0 -178
  120. package/skills/setup.md +0 -107
@@ -1,325 +0,0 @@
1
- # Reference: `SodaxWalletProvider` Config Map
2
-
3
- The biggest single change in v2. v1 spread chain configuration across three props (`rpcConfig`, `options`, `initialState`); v2 collapses them into one `config` prop. See [`../breaking-changes.md`](../breaking-changes.md) §1 for the WHY.
4
-
5
- ---
6
-
7
- ## ⚠️ First, fix the provider stack (silent runtime crash otherwise)
8
-
9
- **v1 created `QueryClient` internally; v2 expects the consumer to provide one.** If you only swap the prop shape without adding `QueryClientProvider`, the app crashes at runtime — React Query hooks throw "No QueryClient set". This is **not** a typecheck error; it surfaces only when a wallet hook mounts.
10
-
11
- ```tsx
12
- // v1 ❌ — QueryClientProvider was internal
13
- <SodaxWalletProvider rpcConfig={...} options={...}>{children}</SodaxWalletProvider>
14
-
15
- // v2 ✅ — caller wraps with QueryClientProvider
16
- <QueryClientProvider client={queryClient}>
17
- <SodaxWalletProvider config={walletConfig}>{children}</SodaxWalletProvider>
18
- </QueryClientProvider>
19
- ```
20
-
21
- Add `@tanstack/react-query 5.x` as a direct dependency if your app didn't already have it. See [`../breaking-changes.md`](../breaking-changes.md) §2.
22
-
23
- When dapp-kit is also in use, the full provider stack is:
24
-
25
- ```tsx
26
- <SodaxProvider config={sodaxConfig}>
27
- <QueryClientProvider client={queryClient}> {/* required wrapper */}
28
- <SodaxWalletProvider config={walletConfig}>
29
- <YourApp />
30
- </SodaxWalletProvider>
31
- </QueryClientProvider>
32
- </SodaxProvider>
33
- ```
34
-
35
- ---
36
-
37
- ## Top-level shape
38
-
39
- ```tsx
40
- // v1 ❌
41
- <SodaxWalletProvider
42
- rpcConfig={rpcConfig}
43
- options={{ wagmi, solana, sui }}
44
- initialState={wagmiState}
45
- >
46
- {children}
47
- </SodaxWalletProvider>
48
-
49
- // v2 ✅
50
- <SodaxWalletProvider config={walletConfig}>{children}</SodaxWalletProvider>
51
- ```
52
-
53
- The v2 `config` is `SodaxWalletConfig`. Top-level keys are **chain-type slots** (`EVM`, `SOLANA`, `SUI`, `BITCOIN`, `STELLAR`, `ICON`, `INJECTIVE`, `NEAR`, `STACKS`). **Omit a slot to skip mounting that adapter**; pass `{}` to mount with SDK defaults.
54
-
55
- ---
56
-
57
- ## v1 `rpcConfig` → v2 per-chain `rpcUrl`
58
-
59
- v1 took a flat dictionary keyed by some chain string:
60
-
61
- ```ts
62
- // v1 ❌
63
- const rpcConfig: RpcConfig = {
64
- 'sonic': 'https://rpc.soniclabs.com',
65
- '0x1.eth': 'https://ethereum-rpc.publicnode.com',
66
- 'solana': 'https://api.mainnet-beta.solana.com',
67
- // ...
68
- };
69
- ```
70
-
71
- v2 nests RPC URLs under `<ChainTypeSlot>.chains[ChainKey].rpcUrl`:
72
-
73
- ```ts
74
- // v2 ✅
75
- import { ChainKeys } from '@sodax/types';
76
- import type { SodaxWalletConfig } from '@sodax/wallet-sdk-react';
77
-
78
- const walletConfig: SodaxWalletConfig = {
79
- EVM: {
80
- chains: {
81
- [ChainKeys.SONIC_MAINNET]: { rpcUrl: 'https://rpc.soniclabs.com' },
82
- [ChainKeys.ETHEREUM_MAINNET]: { rpcUrl: 'https://ethereum-rpc.publicnode.com' },
83
- },
84
- },
85
- SOLANA: {
86
- chains: {
87
- [ChainKeys.SOLANA_MAINNET]: { rpcUrl: 'https://api.mainnet-beta.solana.com' },
88
- },
89
- },
90
- };
91
- ```
92
-
93
- **Per-chain entry shape varies** by chain type:
94
-
95
- | Slot | Per-chain entry |
96
- |---|---|
97
- | `EVM`, `SOLANA`, `SUI`, `ICON`, `NEAR` | `{ rpcUrl?, defaults? }` |
98
- | `BITCOIN`, `STELLAR`, `INJECTIVE` | extends their `*RpcConfig` type with `{ defaults? }` |
99
- | `STACKS` | preset name (string) **or** `StacksNetworkLike & { defaults? }` |
100
-
101
- The single source of truth for the per-chain shape is `ChainMeta` in `src/types/config.ts` — `SodaxWalletConfig`, `ChainEntry<K>`, `WalletDefaultsByKey<K>` derive from it.
102
-
103
- ---
104
-
105
- ## v1 `options.wagmi` → v2 `EVM.*`
106
-
107
- ```ts
108
- // v1 ❌
109
- options: {
110
- wagmi: {
111
- reconnectOnMount: false,
112
- ssr: true,
113
- },
114
- }
115
- ```
116
-
117
- ```ts
118
- // v2 ✅
119
- EVM: {
120
- ssr: true,
121
- reconnectOnMount: true,
122
- chains: { ... },
123
- }
124
- ```
125
-
126
- | v1 | v2 | Notes |
127
- |---|---|---|
128
- | `options.wagmi.ssr` | `EVM.ssr` | Defaults: v1 `true`, v2 not set (caller decides). For Next.js, set `true`. |
129
- | `options.wagmi.reconnectOnMount` | `EVM.reconnectOnMount` | Still supported, default `false`. Moved into the `EVM` slot. See [`migration/recipes/ssr-setup.md`](../recipes/ssr-setup.md) for the SSR-aware flow. |
130
-
131
- ---
132
-
133
- ## v1 `options.solana` → v2 `SOLANA.*`
134
-
135
- ```ts
136
- // v1 ❌
137
- options: {
138
- solana: { autoConnect: true },
139
- }
140
- ```
141
-
142
- ```ts
143
- // v2 ✅
144
- SOLANA: {
145
- autoConnect: true,
146
- chains: {
147
- [ChainKeys.SOLANA_MAINNET]: { rpcUrl: '...' },
148
- },
149
- }
150
- ```
151
-
152
- | v1 | v2 |
153
- |---|---|
154
- | `options.solana.autoConnect` | `SOLANA.autoConnect` |
155
-
156
- ---
157
-
158
- ## v1 `options.sui` → v2 `SUI.*`
159
-
160
- ```ts
161
- // v1 ❌
162
- options: {
163
- sui: { autoConnect: true },
164
- }
165
- ```
166
-
167
- ```ts
168
- // v2 ✅
169
- SUI: {
170
- autoConnect: true,
171
- network: 'mainnet',
172
- }
173
- ```
174
-
175
- `SUI` slot fields (per `SuiAdapterFields` in `src/types/config.ts`):
176
-
177
- | Field | Type | Default |
178
- |---|---|---|
179
- | `autoConnect` | `boolean?` | `true` |
180
- | `network` | `'mainnet' \| 'testnet' \| 'devnet'?` | `'mainnet'` |
181
-
182
- | v1 | v2 |
183
- |---|---|
184
- | `options.sui.autoConnect` | `SUI.autoConnect` |
185
-
186
- ---
187
-
188
- ## v1 `initialState` → v2 `EVM.initialState`
189
-
190
- v1 accepted a top-level `initialState` prop (`WagmiState`) for SSR hydration. v2 still accepts the same value — it just lives **inside the `EVM` slot** of the `config` prop.
191
-
192
- ```ts
193
- // v1 ❌
194
- <SodaxWalletProvider rpcConfig={...} options={...} initialState={wagmiState}>
195
-
196
- // v2 ✅
197
- <SodaxWalletProvider config={{
198
- EVM: {
199
- ssr: true,
200
- initialState: wagmiState,
201
- chains: {...},
202
- },
203
- /* ... */
204
- }}>
205
- ```
206
-
207
- If you previously derived `initialState` via `cookieToInitialState(...)` in a server component, keep that logic — just pass the result into `EVM.initialState` instead of the top-level prop. See [`../recipes/ssr-setup.md`](../recipes/ssr-setup.md) for a full Next.js example.
208
-
209
- ---
210
-
211
- ## New in v2: per-chain `defaults`
212
-
213
- Each chain entry can hold call-level defaults that flow to the bridged `IXxxWalletProvider`:
214
-
215
- ```ts
216
- // v2 ✅
217
- EVM: {
218
- chains: {
219
- [ChainKeys.ARBITRUM_MAINNET]: {
220
- rpcUrl: 'https://arb1.arbitrum.io/rpc',
221
- defaults: {
222
- waitForTransactionReceipt: { confirmations: 1 },
223
- },
224
- },
225
- },
226
- }
227
- ```
228
-
229
- The `defaults` shape is per-chain — the type system narrows what's valid per chain key. Inspect `WalletDefaultsByKey<K>` in `src/types/config.ts`.
230
-
231
- ---
232
-
233
- ## New in v2: `EVM.walletConnect`
234
-
235
- WalletConnect support for EVM — extends wagmi's `WalletConnectParameters`:
236
-
237
- ```ts
238
- // v2 ✅
239
- EVM: {
240
- walletConnect: {
241
- projectId: 'wc-cloud-project-id', // required from cloud.walletconnect.com
242
- // qrModalOptions, isNewChainsStale, etc. — full WalletConnectParameters
243
- },
244
- }
245
- ```
246
-
247
- When `walletConnect` is present, a WalletConnect connector is added to the wagmi config; `useXConnectors({ xChainType: 'EVM' })` will surface it. Omitting the field preserves v1 behavior (EIP-6963 only).
248
-
249
- See [`../recipes/walletconnect-migration.md`](../recipes/walletconnect-migration.md).
250
-
251
- ---
252
-
253
- ## New in v2: `<chainSlot>.connectors?` override
254
-
255
- Each chain slot accepts an optional `connectors` field to override the default connector list. Most consumers don't need this — defaults work for all common wallet vendors.
256
-
257
- ---
258
-
259
- ## Frozen on first render
260
-
261
- `SodaxWalletProvider` captures `config` once on mount and ignores prop-reference changes. To swap config at runtime, remount with a new `key`:
262
-
263
- ```tsx
264
- // v2 ✅
265
- <SodaxWalletProvider key={configVersion} config={walletConfig}>
266
- {children}
267
- </SodaxWalletProvider>
268
- ```
269
-
270
- Bumping `configVersion` (e.g. when the user picks a new RPC endpoint) forces a clean re-init. See [`../breaking-changes.md`](../breaking-changes.md) §12.
271
-
272
- ---
273
-
274
- ## Provider-stack order changed
275
-
276
- Moved to the top of this file — see the ⚠️ block at the start. The summary: wrap `SodaxWalletProvider` in `QueryClientProvider`, otherwise React Query throws "No QueryClient set" at runtime.
277
-
278
- ---
279
-
280
- ## Minimal valid v2 config — every shape, side by side
281
-
282
- ```ts
283
- // v2 ✅
284
- import { ChainKeys } from '@sodax/types';
285
- import type { SodaxWalletConfig } from '@sodax/wallet-sdk-react';
286
-
287
- const walletConfig: SodaxWalletConfig = {
288
- // EVM — needs chains
289
- EVM: {
290
- ssr: true,
291
- chains: {
292
- [ChainKeys.SONIC_MAINNET]: { rpcUrl: 'https://rpc.soniclabs.com' },
293
- },
294
- // optional:
295
- // walletConnect: { projectId: '...' },
296
- },
297
-
298
- // SOLANA — chain entry + autoConnect
299
- SOLANA: {
300
- autoConnect: false,
301
- chains: {
302
- [ChainKeys.SOLANA_MAINNET]: { rpcUrl: 'https://api.mainnet-beta.solana.com' },
303
- },
304
- },
305
-
306
- // SUI — network preset
307
- SUI: { network: 'mainnet' },
308
-
309
- // ICON — chain entry
310
- ICON: {
311
- chains: {
312
- [ChainKeys.ICON_MAINNET]: { rpcUrl: 'https://ctz.solidwallet.io/api/v3' },
313
- },
314
- },
315
-
316
- // BITCOIN, STELLAR, INJECTIVE, NEAR, STACKS — pass {} to mount with SDK defaults
317
- BITCOIN: {},
318
- STELLAR: {},
319
- INJECTIVE: {},
320
- NEAR: {},
321
- STACKS: {},
322
- };
323
- ```
324
-
325
- Omit any slot to skip that chain entirely.
@@ -1,323 +0,0 @@
1
- # Reference: Hook Signature Map
2
-
3
- Per-hook signature changes for v1 → v2. See [`../breaking-changes.md`](../breaking-changes.md) §3 for the WHY.
4
-
5
- ---
6
-
7
- ## Quick rule
8
-
9
- | Pattern | v1 (positional) | v2 (options object) |
10
- |---|---|---|
11
- | Pass chain family | `useX('EVM')` | `useX({ xChainType: 'EVM' })` |
12
- | Pass chain id | `useX('0x1.eth')` | `useX({ xChainId: ChainKeys.ETHEREUM_MAINNET })` |
13
- | Cannot pass both | (runtime detected) | TypeScript-enforced (`xChainType: never` on the chain-id branch and vice versa) |
14
-
15
- `xChainId` in v2 is typed as `SpokeChainKey` (the enum from `@sodax/types`), not the raw string id. The narrower type lets v2 hooks return chain-typed providers (e.g. `IEvmWalletProvider | undefined`) instead of the v1 union.
16
-
17
- ---
18
-
19
- ## Canonical rule — `xChainId` is non-nullable
20
-
21
- Hooks that take `xChainId` declare it via overloads as required `SpokeChainKey`. Passing a nullable value (e.g. `token?.chainKey`) does not compile.
22
-
23
- The exported `UseWalletProviderOptions` (and similar) is the *implementation* type and looks permissive — but calls resolve against the overloads, which are stricter. Casting around the error is unsafe: it either fails to bypass the overload at all, or silently strips the nullable from the type while the runtime value stays `undefined` (some hooks tolerate that and return `undefined`; `useXAccount` throws).
24
-
25
- Handle the nullable **before** the hook call:
26
-
27
- ```ts
28
- // Narrow, default-fallback, or split into a child component — pick the one that fits.
29
- if (!chainKey) return null;
30
- const wp = useWalletProvider({ xChainId: chainKey });
31
- ```
32
-
33
- ---
34
-
35
- ## `useXAccount`
36
-
37
- ```ts
38
- // v1 ❌
39
- const { address } = useXAccount('EVM');
40
- const { address } = useXAccount('0x1.eth');
41
-
42
- // v2 ✅
43
- import { ChainKeys } from '@sodax/types';
44
-
45
- const { address } = useXAccount({ xChainType: 'EVM' });
46
- const { address } = useXAccount({ xChainId: ChainKeys.ETHEREUM_MAINNET });
47
- ```
48
-
49
- **Decision rule (positional value → field name):** v1 accepted both `ChainType` and `ChainId` and detected at runtime. In v2 you must choose the right field:
50
-
51
- - v1 value is a family literal (`'EVM'`, `'SOLANA'`, …) → v2 `xChainType`.
52
- - v1 value is a chain-key string (`'0x1.eth'`, `XToken.xChainId`, …) → v2 `xChainId` typed as `SpokeChainKey`.
53
- - v1 value comes from `getXChainType(...)` (returns `ChainType | undefined`) → v2 `xChainType`, but **guard against `undefined`** — see below.
54
-
55
- **v2 asserts at runtime that exactly one of `xChainId` / `xChainType` is present.** Both undefined throws `'[useXAccount] pass xChainId or xChainType'`; both present throws `'[useXAccount] pass either xChainId or xChainType, not both'`. v1 was permissive (called with `undefined`, it returned an empty account). v2 is strict.
56
-
57
- **Common nullable patterns and their fixes:**
58
-
59
- ```ts
60
- // ❌ v1 idiom — returns empty account when nothing selected, runs every render
61
- const { address } = useXAccount(selectedChainId ?? undefined);
62
-
63
- // ✅ v2 fix 1 — index a snapshot from useXAccounts (no per-key hook call)
64
- const xAccounts = useXAccounts();
65
- const chainType = selectedChainId ? getXChainType(selectedChainId) : undefined;
66
- const address = chainType ? xAccounts[chainType]?.address : undefined;
67
-
68
- // ✅ v2 fix 2 — supply a sensible default chain key so the hook always has input
69
- const { address } = useXAccount({
70
- xChainId: selectedChainId ?? ChainKeys.SONIC_MAINNET,
71
- });
72
-
73
- // ✅ v2 fix 3 — split into a child component that only mounts when input is known
74
- {selectedChainId ? <Account xChainId={selectedChainId} /> : null}
75
- function Account({ xChainId }: { xChainId: SpokeChainKey }) {
76
- const { address } = useXAccount({ xChainId });
77
- // ...
78
- }
79
- ```
80
-
81
- The other "options-object" hooks (`useXConnection`, `useXConnectors`, `useXService`, `useWalletProvider`) are **lenient** — passing no field returns the empty/undefined value silently. `useXAccount` is the only one that asserts.
82
-
83
- **Other notes:**
84
- - Return shape unchanged: `XAccount = { address, xChainType, publicKey? }`.
85
- - When a valid chain is supplied but no wallet is connected, `address` is `undefined` while `xChainType` is filled — same as v1's connected-empty state.
86
-
87
- ---
88
-
89
- ## `useXConnectors`
90
-
91
- ```ts
92
- // v1 ❌
93
- const connectors = useXConnectors('EVM');
94
-
95
- // v2 ✅
96
- const connectors = useXConnectors({ xChainType: 'EVM' });
97
- ```
98
-
99
- **Other notes:**
100
- - Return type changed from `XConnector[]` to `IXConnector[]` (interface). For most consumers this is invisible — both expose `id`, `name`, `icon`, `xChainType`, `connect()`, `disconnect()`.
101
- - v2 enriches each connector with `isInstalled`, `installUrl`, `icon` (read at access time from `window.*`).
102
- - v2 returns `[]` and logs a one-time warning if the chain isn't in `enabledChains`. v1 returned `[]` silently.
103
-
104
- ---
105
-
106
- ## `useXConnection`
107
-
108
- ```ts
109
- // v1 ❌
110
- const connection = useXConnection('EVM');
111
-
112
- // v2 ✅
113
- const connection = useXConnection({ xChainType: 'EVM' });
114
- ```
115
-
116
- Return shape unchanged: `XConnection | undefined = { xAccount, xConnectorId } | undefined`.
117
-
118
- ---
119
-
120
- ## `useXService`
121
-
122
- ```ts
123
- // v1 ❌
124
- const service = useXService('EVM');
125
-
126
- // v2 ✅
127
- const service = useXService({ xChainType: 'EVM' });
128
- ```
129
-
130
- Return type unchanged.
131
-
132
- > **Need the typed concrete class** (e.g. `EvmXService.publicClient`, `StellarXService.server`, `BitcoinXService` for `instanceof` checks)? The concrete classes (`EvmXService`, `SolanaXService`, `StellarXService`, etc.) are **still exported in v2**, but moved from the package barrel to per-chain sub-paths: `@sodax/wallet-sdk-react/xchains/<chain>`. See [`imports.md` § Concrete chain classes](./imports.md#concrete-chain-classes--moved-behind-sub-path-imports) for the full per-chain table. The TS error `TS2724: '"@sodax/wallet-sdk-react"' has no exported member named 'StellarXService'. Did you mean 'useXService'?` is misleading when you actually need the class — the class is at the sub-path, not the hook.
133
-
134
- ---
135
-
136
- ## `useWalletProvider`
137
-
138
- ```ts
139
- // v1 ❌ — positional spokeChainId, returns wide union
140
- const wp = useWalletProvider('sui');
141
-
142
- // v2 ✅ — options object, narrowest type when xChainId is passed
143
- import { ChainKeys } from '@sodax/types';
144
-
145
- const wp = useWalletProvider({ xChainId: ChainKeys.SUI_MAINNET });
146
- // ^ inferred as ISuiWalletProvider | undefined
147
-
148
- const wp2 = useWalletProvider({ xChainType: 'EVM' });
149
- // ^ inferred as IEvmWalletProvider | undefined
150
-
151
- const wp3 = useWalletProvider();
152
- // ^ inferred as IWalletProvider | undefined (any)
153
- ```
154
-
155
- **Other notes:**
156
- - Pass either `xChainId` (`SpokeChainKey`) or `xChainType` (`ChainType`), never both. Chain key gives narrower TypeScript inference.
157
- - Returns `undefined` when the chain isn't enabled in `walletConfig` (logs a one-time warning) or when no wallet is connected.
158
-
159
- ---
160
-
161
- ## `useXConnect`
162
-
163
- ```ts
164
- // v1 ✅ AND v2 ✅ — same signature
165
- const { mutateAsync: connect } = useXConnect();
166
- await connect(connector);
167
- ```
168
-
169
- No change in shape. The `connector` argument type is `IXConnector` in v2 (was `XConnector` abstract class in v1) — runtime behavior identical for any connector returned by `useXConnectors`.
170
-
171
- ---
172
-
173
- ## `useXDisconnect`
174
-
175
- ```ts
176
- // v1 ❌ — returned function takes positional ChainType
177
- const disconnect = useXDisconnect();
178
- await disconnect('EVM');
179
-
180
- // v2 ✅ — returned function takes options object
181
- const disconnect = useXDisconnect();
182
- await disconnect({ xChainType: 'EVM' });
183
- ```
184
-
185
- The hook itself takes no args in both versions. The **returned function** changed: v1 was positional `(xChainType: ChainType) => Promise<void>`; v2 is `(args: UseXDisconnectArgs) => Promise<void>` where `UseXDisconnectArgs = { xChainType: ChainType }`. Same forward-compat reason as the other hooks — see [`../breaking-changes.md`](../breaking-changes.md) §3.
186
-
187
- **Common breakage:** `await disconnect(xChainType)` raises `TS2345: Argument of type 'string' is not assignable to parameter of type 'UseXDisconnectArgs'`. Wrap in an object.
188
-
189
- ---
190
-
191
- ## `useXSignMessage`
192
-
193
- ```ts
194
- // v1 ✅ AND v2 ✅ — same signature
195
- const { mutateAsync: signMessage } = useXSignMessage();
196
- const sig = await signMessage({ xChainType: 'EVM', message: 'hello' });
197
- ```
198
-
199
- No change.
200
-
201
- ---
202
-
203
- ## `useXAccounts`
204
-
205
- ```ts
206
- // v1
207
- const accounts = useXAccounts();
208
-
209
- // v2 — return type is now strictly typed
210
- const accounts = useXAccounts();
211
- // → Partial<Record<ChainType, XAccount>>
212
- ```
213
-
214
- Call signature unchanged. **Indexing tightened**: `accounts[chainType]` returns `XAccount | undefined`. The index variable must be typed as `ChainType` (not `string` or `any`) — otherwise you'll see `TS7053: Element implicitly has an 'any' type`.
215
-
216
- ```ts
217
- // ❌ FAILS — chainType is `string`, can't index Partial<Record<ChainType, ...>>
218
- const chainType = someStringFromConfig;
219
- const account = accounts[chainType];
220
-
221
- // ✅ FIX 1 — narrow with getXChainType (returns ChainType | undefined)
222
- import { getXChainType } from '@sodax/wallet-sdk-react';
223
- const chainType = getXChainType(chainId);
224
- const account = chainType ? accounts[chainType] : undefined;
225
-
226
- // ✅ FIX 2 — call useXAccount per-chain instead of indexing
227
- const account = useXAccount({ xChainType: 'EVM' });
228
- ```
229
-
230
- ---
231
-
232
- ## `useEvmSwitchChain`
233
-
234
- ```ts
235
- // v1 ❌ — positional `expectedXChainId: ChainId`
236
- const { isWrongChain, handleSwitchChain } = useEvmSwitchChain(chainId);
237
-
238
- // v2 ✅ — options object; `xChainId: SpokeChainKey`
239
- import { ChainKeys } from '@sodax/types';
240
-
241
- const { isWrongChain, handleSwitchChain } = useEvmSwitchChain({
242
- xChainId: ChainKeys.ETHEREUM_MAINNET,
243
- });
244
-
245
- if (isWrongChain) handleSwitchChain();
246
- ```
247
-
248
- **Breaking changes:**
249
-
250
- - **Call shape**: positional → options object. Same forward-compat reason as the other hooks.
251
- - **Parameter type**: `ChainId` → `SpokeChainKey` (rename in `@sodax/types`). If the chain key value comes from `XToken.chainKey` (v2) or any `ChainKeys.*` constant, no value change is needed — the rename is type-only.
252
-
253
- **Return shape is unchanged** — both v1 and v2 return `{ isWrongChain: boolean, handleSwitchChain: () => void }`. The hook compares the connected EVM chain to the chain expected by `xChainId` and exposes `isWrongChain` so UI can render a "switch network" CTA without recomputing.
254
-
255
- **Behavior added in v2:**
256
-
257
- - **Injective + MetaMask auto-switch.** When the user connects to Injective via MetaMask, v2 automatically targets Ethereum mainnet underneath. v1 had no Injective awareness.
258
- - **Safe when EVM is disabled.** v2 returns no-op values (`isWrongChain: false`, `handleSwitchChain: () => {}`) if `walletConfig.EVM` is absent, so UI doesn't need to branch.
259
-
260
- ---
261
-
262
- ## `useEthereumChainId`
263
-
264
- **Removed from the public barrel in v2.** Despite the generic-sounding name, the v1 hook was **Injective + MetaMask specific** — it read the underlying Ethereum chain ID exposed by Injective's wallet strategy and was almost always used to drive the "switch back to Ethereum mainnet" UX. v2 makes the hook internal because `useEvmSwitchChain` now handles that Injective auto-switch case directly.
265
-
266
- Migration:
267
-
268
- ```diff
269
- - // v1 ❌ — manual chain-ID comparison for Injective + MetaMask UX
270
- - import { useEthereumChainId } from '@sodax/wallet-sdk-react';
271
- - const chainId = useEthereumChainId();
272
- - if (chainId !== 1) /* prompt user to switch to Ethereum mainnet */;
273
- + // v2 ✅ — useEvmSwitchChain auto-handles Injective + MetaMask underneath
274
- + import { useEvmSwitchChain } from '@sodax/wallet-sdk-react';
275
- + import { ChainKeys } from '@sodax/types';
276
- + const { isWrongChain, handleSwitchChain } = useEvmSwitchChain({
277
- + xChainId: ChainKeys.INJECTIVE_MAINNET,
278
- + });
279
- + if (isWrongChain) handleSwitchChain();
280
- ```
281
-
282
- If you genuinely need the raw EVM chain ID (rare — almost no usage outside the Injective case), wagmi's `useAccount().chainId` is the underlying source. Prefer staying inside `@sodax/wallet-sdk-react` hooks where possible.
283
-
284
- ---
285
-
286
- ## Removed in v2
287
-
288
- | Hook / symbol | Replacement |
289
- |---|---|
290
- | `useXBalances` | Moved to `@sodax/dapp-kit` **AND signature changed**. See note below. |
291
- | `useXWagmiStore` | Removed entirely — direct store reads are not part of the v2 API. Use public hooks (`useXServices`, `useXConnections`, `useXService({ xChainType })`, `useXConnection({ xChainType })`, etc.). See note below. |
292
- | Concrete X-service / X-connector classes (`EvmXService`, `SolanaXService`, `StellarXService`, `BitcoinXService`, `IconXService`, `InjectiveXService`, `SuiXService`, `NearXService`, `StacksXService`, `XverseXConnector`, `UnisatXConnector`, `OKXXConnector`, `IconHanaXConnector`, …) | **Not removed — moved to per-chain sub-paths.** TS error `TS2724: '"@sodax/wallet-sdk-react"' has no exported member named 'StellarXService'. Did you mean 'useXService'?` is misleading — the hint points at `useXService` (which returns the abstract `XService \| undefined`), but the typed class itself lives at `@sodax/wallet-sdk-react/xchains/<chain>`. See [`imports.md` § "Concrete chain classes — moved behind sub-path imports"](./imports.md#concrete-chain-classes--moved-behind-sub-path-imports) for the per-chain table. |
293
-
294
- ### `useXBalances` — moved + reshaped
295
-
296
- Not a simple package-rename. The v2 hook also wraps params and adds a required `xService` field:
297
-
298
- ```diff
299
- - // v1 — flat args from @sodax/wallet-sdk-react
300
- - import { useXBalances } from '@sodax/wallet-sdk-react';
301
- - const { data } = useXBalances({ xChainId, xTokens, address });
302
-
303
- + // v2 — from @sodax/dapp-kit; params wrapped; xService required
304
- + import { useXBalances } from '@sodax/dapp-kit';
305
- + import { useXService, getXChainType } from '@sodax/wallet-sdk-react';
306
- +
307
- + const xService = useXService({ xChainType: getXChainType(xChainId) });
308
- + const { data } = useXBalances({
309
- + params: { xService, xChainId, xTokens, address },
310
- + });
311
- ```
312
-
313
- The `xService` injection is part of dapp-kit's "no implicit wallet-sdk dependency" design — dapp-kit doesn't import from `wallet-sdk-react`, so the consumer wires the service across at the call site. See `@sodax/dapp-kit/ai-exported/integration/architecture.md` § "Decoupling from wallet-sdk-react".
314
-
315
- ### `useXWagmiStore` — removed (store reads moved to public hooks)
316
-
317
- The v1 Zustand store hook is gone from the v2 barrel. Direct store access is no longer supported. Every `useXWagmiStore(state => state.X)` selector maps to a public hook — see [`imports.md` § "Store hook removed from the public API"](./imports.md#store-hook-removed-from-the-public-api) for the full field-to-hook map and decision tree (`state.xServices` → `useXServices()`, `state.xConnections[chainType]` → `useXConnection({ xChainType })`, etc.). The localStorage key (`xwagmi-store`) is unchanged, so user connections survive the upgrade.
318
-
319
- ---
320
-
321
- ## Added in v2 (no v1 equivalent)
322
-
323
- See [`imports.md`](./imports.md) § "Added in v2" for the full list (`useWalletModal`, `useChainGroups`, `useBatchConnect`, etc.). These are not migration items — they are new capabilities you may opt into.