@sodax/wallet-sdk-react 2.0.0-rc.2 → 2.0.0-rc.21

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 (141) hide show
  1. package/README.md +14 -7
  2. package/dist/{XConnector-B9YQTVJ4.d.ts → XConnector-12q0OVe5.d.ts} +1 -1
  3. package/dist/{chunk-NY7U7OJW.mjs → chunk-7V7O3Q7Y.mjs} +0 -2
  4. package/dist/{chunk-BXJLBR4G.mjs → chunk-C6M34IVL.mjs} +2 -4
  5. package/dist/{chunk-PLCA4ZDJ.mjs → chunk-HEY7NRFG.mjs} +254 -52
  6. package/dist/{chunk-X2MHIWXO.mjs → chunk-IFXZQW4C.mjs} +0 -2
  7. package/dist/{chunk-7ULB6DW4.mjs → chunk-JQ4H4GJ5.mjs} +3 -5
  8. package/dist/{chunk-E5IAZ7E6.mjs → chunk-LM7R5APS.mjs} +22 -11
  9. package/dist/{chunk-BKJB527E.mjs → chunk-MWWVB7TD.mjs} +2 -4
  10. package/dist/{chunk-MXZVF5HR.mjs → chunk-NAKCAL2M.mjs} +0 -2
  11. package/dist/{chunk-PJLEJVAU.mjs → chunk-OPX55MQO.mjs} +11 -6
  12. package/dist/chunk-QMXBY3UI.mjs +1 -0
  13. package/dist/{chunk-MAQ47Q52.mjs → chunk-TACW7Z4D.mjs} +0 -2
  14. package/dist/chunk-VPNYFVP6.mjs +53 -0
  15. package/dist/{chunk-2BOUGCJ7.mjs → chunk-WPZOLGVB.mjs} +4 -6
  16. package/dist/{chunk-66BAUK56.mjs → chunk-X7BHR7WS.mjs} +4 -6
  17. package/dist/{chunk-N5A2TMF6.mjs → chunk-XJA2MJMG.mjs} +2 -4
  18. package/dist/{config-OlnzyEUE.d.ts → config-DEsqgrG1.d.ts} +10 -5
  19. package/dist/index.d.ts +6 -6
  20. package/dist/index.mjs +20 -31
  21. package/dist/xchains/bitcoin/index.d.ts +20 -3
  22. package/dist/xchains/bitcoin/index.mjs +14 -16
  23. package/dist/xchains/evm/index.d.ts +3 -3
  24. package/dist/xchains/evm/index.mjs +3 -5
  25. package/dist/xchains/icon/index.d.ts +1 -1
  26. package/dist/xchains/icon/index.mjs +5 -7
  27. package/dist/xchains/injective/index.d.ts +2 -2
  28. package/dist/xchains/injective/index.mjs +3 -5
  29. package/dist/xchains/near/index.d.ts +1 -1
  30. package/dist/xchains/near/index.mjs +4 -6
  31. package/dist/xchains/solana/index.d.ts +1 -1
  32. package/dist/xchains/solana/index.mjs +5 -7
  33. package/dist/xchains/stacks/index.d.ts +10 -4
  34. package/dist/xchains/stacks/index.mjs +3 -5
  35. package/dist/xchains/stellar/index.d.ts +1 -1
  36. package/dist/xchains/stellar/index.mjs +4 -6
  37. package/dist/xchains/sui/index.d.ts +1 -1
  38. package/dist/xchains/sui/index.mjs +5 -7
  39. package/package.json +37 -29
  40. package/ai-exported/AGENTS.md +0 -122
  41. package/ai-exported/integration/README.md +0 -102
  42. package/ai-exported/integration/ai-rules.md +0 -136
  43. package/ai-exported/integration/architecture.md +0 -181
  44. package/ai-exported/integration/examples/01-minimal-evm.tsx +0 -75
  45. package/ai-exported/integration/examples/02-multi-chain-modal.tsx +0 -169
  46. package/ai-exported/integration/examples/03-nextjs-app-router.tsx +0 -99
  47. package/ai-exported/integration/examples/04-walletconnect-setup.tsx +0 -89
  48. package/ai-exported/integration/examples/README.md +0 -29
  49. package/ai-exported/integration/recipes/batch-operations.md +0 -223
  50. package/ai-exported/integration/recipes/bridge-to-sdk.md +0 -164
  51. package/ai-exported/integration/recipes/chain-detection.md +0 -254
  52. package/ai-exported/integration/recipes/connect-button.md +0 -156
  53. package/ai-exported/integration/recipes/multi-chain-modal.md +0 -199
  54. package/ai-exported/integration/recipes/setup.md +0 -158
  55. package/ai-exported/integration/recipes/sign-message.md +0 -137
  56. package/ai-exported/integration/recipes/sub-path-imports.md +0 -95
  57. package/ai-exported/integration/recipes/switch-chain.md +0 -141
  58. package/ai-exported/integration/recipes/walletconnect-setup.md +0 -139
  59. package/ai-exported/integration/reference/api-surface.md +0 -175
  60. package/ai-exported/integration/reference/chain-support.md +0 -78
  61. package/ai-exported/integration/reference/connectors.md +0 -74
  62. package/ai-exported/integration/reference/hooks.md +0 -204
  63. package/ai-exported/integration/reference/wallet-brands.md +0 -106
  64. package/ai-exported/migration/README.md +0 -49
  65. package/ai-exported/migration/ai-rules.md +0 -144
  66. package/ai-exported/migration/breaking-changes.md +0 -305
  67. package/ai-exported/migration/checklist.md +0 -159
  68. package/ai-exported/migration/recipes/connect-button.md +0 -166
  69. package/ai-exported/migration/recipes/multi-chain-modal.md +0 -244
  70. package/ai-exported/migration/recipes/ssr-setup.md +0 -162
  71. package/ai-exported/migration/recipes/walletconnect-migration.md +0 -168
  72. package/ai-exported/migration/reference/components.md +0 -73
  73. package/ai-exported/migration/reference/config.md +0 -307
  74. package/ai-exported/migration/reference/hooks.md +0 -278
  75. package/ai-exported/migration/reference/imports.md +0 -157
  76. package/dist/chunk-2BOUGCJ7.mjs.map +0 -1
  77. package/dist/chunk-66BAUK56.mjs.map +0 -1
  78. package/dist/chunk-7ULB6DW4.mjs.map +0 -1
  79. package/dist/chunk-BKJB527E.mjs.map +0 -1
  80. package/dist/chunk-BXJLBR4G.mjs.map +0 -1
  81. package/dist/chunk-E5IAZ7E6.mjs.map +0 -1
  82. package/dist/chunk-MAQ47Q52.mjs.map +0 -1
  83. package/dist/chunk-MXZVF5HR.mjs.map +0 -1
  84. package/dist/chunk-N5A2TMF6.mjs.map +0 -1
  85. package/dist/chunk-NY7U7OJW.mjs.map +0 -1
  86. package/dist/chunk-PJLEJVAU.mjs.map +0 -1
  87. package/dist/chunk-PLCA4ZDJ.mjs.map +0 -1
  88. package/dist/chunk-TZMKDXFA.mjs +0 -3
  89. package/dist/chunk-TZMKDXFA.mjs.map +0 -1
  90. package/dist/chunk-X2MHIWXO.mjs.map +0 -1
  91. package/dist/chunk-XZ7CHO2S.mjs +0 -41
  92. package/dist/chunk-XZ7CHO2S.mjs.map +0 -1
  93. package/dist/index.cjs +0 -3337
  94. package/dist/index.cjs.map +0 -1
  95. package/dist/index.mjs.map +0 -1
  96. package/dist/xchains/bitcoin/index.cjs +0 -1927
  97. package/dist/xchains/bitcoin/index.cjs.map +0 -1
  98. package/dist/xchains/bitcoin/index.mjs.map +0 -1
  99. package/dist/xchains/evm/index.cjs +0 -316
  100. package/dist/xchains/evm/index.cjs.map +0 -1
  101. package/dist/xchains/evm/index.mjs.map +0 -1
  102. package/dist/xchains/icon/index.cjs +0 -311
  103. package/dist/xchains/icon/index.cjs.map +0 -1
  104. package/dist/xchains/icon/index.mjs.map +0 -1
  105. package/dist/xchains/injective/index.cjs +0 -223
  106. package/dist/xchains/injective/index.cjs.map +0 -1
  107. package/dist/xchains/injective/index.mjs.map +0 -1
  108. package/dist/xchains/near/index.cjs +0 -190
  109. package/dist/xchains/near/index.cjs.map +0 -1
  110. package/dist/xchains/near/index.mjs.map +0 -1
  111. package/dist/xchains/solana/index.cjs +0 -186
  112. package/dist/xchains/solana/index.cjs.map +0 -1
  113. package/dist/xchains/solana/index.mjs.map +0 -1
  114. package/dist/xchains/stacks/index.cjs +0 -240
  115. package/dist/xchains/stacks/index.cjs.map +0 -1
  116. package/dist/xchains/stacks/index.mjs.map +0 -1
  117. package/dist/xchains/stellar/index.cjs +0 -322
  118. package/dist/xchains/stellar/index.cjs.map +0 -1
  119. package/dist/xchains/stellar/index.mjs.map +0 -1
  120. package/dist/xchains/sui/index.cjs +0 -248
  121. package/dist/xchains/sui/index.cjs.map +0 -1
  122. package/dist/xchains/sui/index.mjs.map +0 -1
  123. package/docs/ADDING_A_NEW_CHAIN.md +0 -440
  124. package/docs/ARCHITECTURE.md +0 -291
  125. package/docs/BATCH_OPERATIONS.md +0 -267
  126. package/docs/CHAIN_DETECTION.md +0 -216
  127. package/docs/CONFIGURE_PROVIDER.md +0 -360
  128. package/docs/CONNECTORS.md +0 -247
  129. package/docs/CONNECT_FLOW.md +0 -276
  130. package/docs/EVM_SWITCH_CHAIN.md +0 -161
  131. package/docs/SIGN_MESSAGE.md +0 -213
  132. package/docs/SUB_PATH_EXPORTS.md +0 -246
  133. package/docs/WALLETCONNECT.md +0 -154
  134. package/docs/WALLET_MODAL.md +0 -331
  135. package/docs/WALLET_PROVIDER_BRIDGE.md +0 -226
  136. package/skills/SKILLS.md +0 -84
  137. package/skills/bridge-to-sdk.md +0 -148
  138. package/skills/connect-button.md +0 -116
  139. package/skills/evm-only-walletconnect.md +0 -111
  140. package/skills/multi-chain-modal.md +0 -178
  141. package/skills/setup.md +0 -107
@@ -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
- ```
@@ -1,156 +0,0 @@
1
- # Recipe: Connect Button
2
-
3
- Single-chain connect/disconnect button — pick a connector, connect, read the account, disconnect. Self-contained.
4
-
5
- **Depends on:** [`setup.md`](./setup.md)
6
-
7
- ---
8
-
9
- ## Hooks used
10
-
11
- | Hook | Purpose |
12
- |------|---------|
13
- | `useXConnectors({ xChainType })` | List available connectors for the chain family |
14
- | `useXConnect()` | React Query mutation — `mutate(connector)` |
15
- | `useXAccount({ xChainType })` | Read connected account (always returns object — `address` is `undefined` when disconnected) |
16
- | `useXDisconnect()` | Returns `(xChainType) => Promise<void>` |
17
- | `sortConnectors(list, { preferred })` | Optional — rank installed/preferred wallets first |
18
-
19
- ---
20
-
21
- ## Connect button
22
-
23
- ```tsx
24
- 'use client';
25
-
26
- import {
27
- useXConnectors,
28
- useXConnect,
29
- useXAccount,
30
- useXDisconnect,
31
- sortConnectors,
32
- type IXConnector,
33
- } from '@sodax/wallet-sdk-react';
34
-
35
- const PREFERRED = ['hana', 'metamask'] as const;
36
-
37
- export function EvmConnectButton() {
38
- const raw = useXConnectors({ xChainType: 'EVM' });
39
- const connectors = sortConnectors(raw, { preferred: PREFERRED });
40
- const { mutateAsync: connect, isPending, error } = useXConnect();
41
- const account = useXAccount({ xChainType: 'EVM' });
42
- const disconnect = useXDisconnect();
43
-
44
- if (account.address) {
45
- return (
46
- <div>
47
- <code>{account.address}</code>
48
- <button onClick={() => disconnect({ xChainType: 'EVM' })}>Disconnect</button>
49
- </div>
50
- );
51
- }
52
-
53
- return (
54
- <div>
55
- {connectors.map((connector) => (
56
- <button
57
- key={connector.id}
58
- onClick={() => connect(connector).catch(() => {})}
59
- disabled={isPending}
60
- >
61
- {connector.icon && <img src={connector.icon} alt="" width={20} height={20} />}
62
- {connector.name}
63
- {!connector.isInstalled && ' (not installed)'}
64
- </button>
65
- ))}
66
- {error && <p style={{ color: 'red' }}>{error.message}</p>}
67
- </div>
68
- );
69
- }
70
- ```
71
-
72
- ---
73
-
74
- ## Install CTA for missing wallets
75
-
76
- ```tsx
77
- {connectors.map((connector) =>
78
- connector.isInstalled ? (
79
- <button key={connector.id} onClick={() => connect(connector)}>
80
- {connector.name}
81
- </button>
82
- ) : (
83
- <a key={connector.id} href={connector.installUrl} target="_blank" rel="noreferrer">
84
- Install {connector.name}
85
- </a>
86
- ),
87
- )}
88
- ```
89
-
90
- `isInstalled` reads `window.*` at render time (no extra subscription). For batch install detection across wallet brands, use `useIsWalletInstalled` — see [`batch-operations.md`](./batch-operations.md).
91
-
92
- ---
93
-
94
- ## Caveat — provider-managed chains resolve with `undefined`
95
-
96
- For EVM, Solana, and Sui, `connect(connector)` resolves with `undefined` because connection state is set asynchronously after the native SDK adapter reports `connected`. Always read the account via `useXAccount`, not the mutation's return value:
97
-
98
- ```typescript
99
- const { mutateAsync: connect } = useXConnect();
100
- const account = useXAccount({ xChainType: 'EVM' });
101
-
102
- await connect(connector); // resolves with undefined for EVM
103
- // account.address is populated on the next render
104
- ```
105
-
106
- Non-provider chains (Bitcoin, ICON, Injective, Stellar, NEAR, Stacks) return the resolved `XAccount` from `connect()` — but reading via `useXAccount` works for both, so default to it.
107
-
108
- ---
109
-
110
- ## Multiple chains, one button
111
-
112
- For a "connect EVM + Solana + Bitcoin in one click" pattern, use [`batch-operations.md`](./batch-operations.md):
113
-
114
- ```tsx
115
- import { useBatchConnect } from '@sodax/wallet-sdk-react';
116
-
117
- const { run, status } = useBatchConnect({ connectors: ['hana'] });
118
- // Connects every chain Hana supports — sequential, errors collected.
119
- <button onClick={run} disabled={status === 'running'}>
120
- Connect Hana on all chains
121
- </button>;
122
- ```
123
-
124
- ---
125
-
126
- ## Variations
127
-
128
- ### Per-chain-id button (e.g. one button per EVM network)
129
-
130
- ⚠️ Not recommended for EVM — wagmi treats EVM as a **single connection across every configured network**. A per-Ethereum button rarely matches user expectations. If you really need per-chain-id resolution:
131
-
132
- ```tsx
133
- import { ChainKeys } from '@sodax/types';
134
-
135
- const account = useXAccount({ xChainId: ChainKeys.ETHEREUM_MAINNET });
136
- ```
137
-
138
- ### Sui / Solana / etc.
139
-
140
- Replace `'EVM'` with the chain type the button targets:
141
-
142
- ```tsx
143
- const connectors = useXConnectors({ xChainType: 'SUI' });
144
- const { address } = useXAccount({ xChainType: 'SUI' });
145
- ```
146
-
147
- ---
148
-
149
- ## Verification
150
-
151
- ```bash
152
- # 1. Type check
153
- pnpm checkTs
154
-
155
- # 2. Manual — load page, click connect, confirm address renders, reload page, confirm connection survives
156
- ```