@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.
- package/README.md +14 -7
- package/dist/{XConnector-B9YQTVJ4.d.ts → XConnector-12q0OVe5.d.ts} +1 -1
- package/dist/{chunk-NY7U7OJW.mjs → chunk-7V7O3Q7Y.mjs} +0 -2
- package/dist/{chunk-BXJLBR4G.mjs → chunk-C6M34IVL.mjs} +2 -4
- package/dist/{chunk-PLCA4ZDJ.mjs → chunk-HEY7NRFG.mjs} +254 -52
- package/dist/{chunk-X2MHIWXO.mjs → chunk-IFXZQW4C.mjs} +0 -2
- package/dist/{chunk-7ULB6DW4.mjs → chunk-JQ4H4GJ5.mjs} +3 -5
- package/dist/{chunk-E5IAZ7E6.mjs → chunk-LM7R5APS.mjs} +22 -11
- package/dist/{chunk-BKJB527E.mjs → chunk-MWWVB7TD.mjs} +2 -4
- package/dist/{chunk-MXZVF5HR.mjs → chunk-NAKCAL2M.mjs} +0 -2
- package/dist/{chunk-PJLEJVAU.mjs → chunk-OPX55MQO.mjs} +11 -6
- package/dist/chunk-QMXBY3UI.mjs +1 -0
- package/dist/{chunk-MAQ47Q52.mjs → chunk-TACW7Z4D.mjs} +0 -2
- package/dist/chunk-VPNYFVP6.mjs +53 -0
- package/dist/{chunk-2BOUGCJ7.mjs → chunk-WPZOLGVB.mjs} +4 -6
- package/dist/{chunk-66BAUK56.mjs → chunk-X7BHR7WS.mjs} +4 -6
- package/dist/{chunk-N5A2TMF6.mjs → chunk-XJA2MJMG.mjs} +2 -4
- package/dist/{config-OlnzyEUE.d.ts → config-DEsqgrG1.d.ts} +10 -5
- package/dist/index.d.ts +6 -6
- package/dist/index.mjs +20 -31
- package/dist/xchains/bitcoin/index.d.ts +20 -3
- package/dist/xchains/bitcoin/index.mjs +14 -16
- package/dist/xchains/evm/index.d.ts +3 -3
- package/dist/xchains/evm/index.mjs +3 -5
- package/dist/xchains/icon/index.d.ts +1 -1
- package/dist/xchains/icon/index.mjs +5 -7
- package/dist/xchains/injective/index.d.ts +2 -2
- package/dist/xchains/injective/index.mjs +3 -5
- package/dist/xchains/near/index.d.ts +1 -1
- package/dist/xchains/near/index.mjs +4 -6
- package/dist/xchains/solana/index.d.ts +1 -1
- package/dist/xchains/solana/index.mjs +5 -7
- package/dist/xchains/stacks/index.d.ts +10 -4
- package/dist/xchains/stacks/index.mjs +3 -5
- package/dist/xchains/stellar/index.d.ts +1 -1
- package/dist/xchains/stellar/index.mjs +4 -6
- package/dist/xchains/sui/index.d.ts +1 -1
- package/dist/xchains/sui/index.mjs +5 -7
- package/package.json +37 -29
- 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 -158
- 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 -162
- 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 -307
- package/ai-exported/migration/reference/hooks.md +0 -278
- 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 +0 -41
- 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/docs/ADDING_A_NEW_CHAIN.md +0 -440
- package/docs/ARCHITECTURE.md +0 -291
- package/docs/BATCH_OPERATIONS.md +0 -267
- package/docs/CHAIN_DETECTION.md +0 -216
- package/docs/CONFIGURE_PROVIDER.md +0 -360
- package/docs/CONNECTORS.md +0 -247
- package/docs/CONNECT_FLOW.md +0 -276
- package/docs/EVM_SWITCH_CHAIN.md +0 -161
- package/docs/SIGN_MESSAGE.md +0 -213
- package/docs/SUB_PATH_EXPORTS.md +0 -246
- package/docs/WALLETCONNECT.md +0 -154
- package/docs/WALLET_MODAL.md +0 -331
- package/docs/WALLET_PROVIDER_BRIDGE.md +0 -226
- 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,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Multi-chain headless wallet modal.
|
|
3
|
-
* Renders chain picker → wallet picker → connecting → success/error using `useWalletModal`.
|
|
4
|
-
*
|
|
5
|
-
* UI uses native <dialog> — replace with shadcn/Radix/your dialog primitive.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
9
|
-
import {
|
|
10
|
-
SodaxWalletProvider,
|
|
11
|
-
type SodaxWalletConfig,
|
|
12
|
-
useWalletModal,
|
|
13
|
-
useChainGroups,
|
|
14
|
-
useXConnectors,
|
|
15
|
-
sortConnectors,
|
|
16
|
-
type IXConnector,
|
|
17
|
-
} from '@sodax/wallet-sdk-react';
|
|
18
|
-
import type { ChainType } from '@sodax/types';
|
|
19
|
-
import { ChainKeys } from '@sodax/types';
|
|
20
|
-
|
|
21
|
-
const queryClient = new QueryClient();
|
|
22
|
-
|
|
23
|
-
const walletConfig: SodaxWalletConfig = {
|
|
24
|
-
EVM: {
|
|
25
|
-
chains: {
|
|
26
|
-
[ChainKeys.SONIC_MAINNET]: { rpcUrl: 'https://rpc.soniclabs.com' },
|
|
27
|
-
[ChainKeys.ETHEREUM_MAINNET]: { rpcUrl: 'https://ethereum-rpc.publicnode.com' },
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
SOLANA: {
|
|
31
|
-
chains: { [ChainKeys.SOLANA_MAINNET]: { rpcUrl: 'https://api.mainnet-beta.solana.com' } },
|
|
32
|
-
},
|
|
33
|
-
SUI: { network: 'mainnet' },
|
|
34
|
-
BITCOIN: {},
|
|
35
|
-
ICON: {
|
|
36
|
-
chains: { [ChainKeys.ICON_MAINNET]: { rpcUrl: 'https://ctz.solidwallet.io/api/v3' } },
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export function App() {
|
|
41
|
-
return (
|
|
42
|
-
<QueryClientProvider client={queryClient}>
|
|
43
|
-
<SodaxWalletProvider config={walletConfig}>
|
|
44
|
-
<WalletModalRoot />
|
|
45
|
-
</SodaxWalletProvider>
|
|
46
|
-
</QueryClientProvider>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function WalletModalRoot() {
|
|
51
|
-
const modal = useWalletModal({
|
|
52
|
-
onConnected: (chainType, account) => {
|
|
53
|
-
console.log('connected', chainType, account.address);
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
switch (modal.state.kind) {
|
|
58
|
-
case 'closed':
|
|
59
|
-
return (
|
|
60
|
-
<button type="button" onClick={modal.open}>
|
|
61
|
-
Connect Wallet
|
|
62
|
-
</button>
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
case 'chainSelect':
|
|
66
|
-
return <ChainPicker onPick={modal.selectChain} onClose={modal.close} />;
|
|
67
|
-
|
|
68
|
-
case 'walletSelect':
|
|
69
|
-
return (
|
|
70
|
-
<WalletPicker
|
|
71
|
-
chainType={modal.state.chainType}
|
|
72
|
-
onPick={modal.selectWallet}
|
|
73
|
-
onBack={modal.back}
|
|
74
|
-
onClose={modal.close}
|
|
75
|
-
/>
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
case 'connecting':
|
|
79
|
-
// Hide modal while wagmi's QR modal owns the screen for WalletConnect
|
|
80
|
-
if (modal.state.connector.id === 'walletConnect') return null;
|
|
81
|
-
return (
|
|
82
|
-
<Dialog onClose={modal.close}>
|
|
83
|
-
<p>Approve in {modal.state.connector.name}…</p>
|
|
84
|
-
<button type="button" onClick={modal.back}>
|
|
85
|
-
Cancel
|
|
86
|
-
</button>
|
|
87
|
-
</Dialog>
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
case 'error':
|
|
91
|
-
return (
|
|
92
|
-
<Dialog onClose={modal.close}>
|
|
93
|
-
<p>Error: {modal.state.error.message}</p>
|
|
94
|
-
{!modal.state.connector.isInstalled && modal.state.connector.installUrl && (
|
|
95
|
-
<a href={modal.state.connector.installUrl} target="_blank" rel="noreferrer">
|
|
96
|
-
Install {modal.state.connector.name}
|
|
97
|
-
</a>
|
|
98
|
-
)}
|
|
99
|
-
<button type="button" onClick={modal.retry}>
|
|
100
|
-
Retry
|
|
101
|
-
</button>
|
|
102
|
-
<button type="button" onClick={modal.back}>
|
|
103
|
-
Pick another wallet
|
|
104
|
-
</button>
|
|
105
|
-
</Dialog>
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
case 'success':
|
|
109
|
-
// onConnected fired; close the modal
|
|
110
|
-
queueMicrotask(modal.close);
|
|
111
|
-
return null;
|
|
112
|
-
|
|
113
|
-
default:
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function ChainPicker({ onPick, onClose }: { onPick: (c: ChainType) => void; onClose: () => void }) {
|
|
119
|
-
const groups = useChainGroups({ order: ['EVM', 'SOLANA', 'SUI', 'BITCOIN', 'ICON'] });
|
|
120
|
-
return (
|
|
121
|
-
<Dialog onClose={onClose}>
|
|
122
|
-
<h2>Select a chain</h2>
|
|
123
|
-
{groups.map(group => (
|
|
124
|
-
<button type="button" key={group.chainType} onClick={() => onPick(group.chainType)}>
|
|
125
|
-
<span>{group.displayName}</span>
|
|
126
|
-
{group.isConnected && ' ✓'}
|
|
127
|
-
</button>
|
|
128
|
-
))}
|
|
129
|
-
</Dialog>
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const PREFERRED = ['hana', 'metamask', 'phantom'] as const;
|
|
134
|
-
|
|
135
|
-
function WalletPicker({
|
|
136
|
-
chainType,
|
|
137
|
-
onPick,
|
|
138
|
-
onBack,
|
|
139
|
-
onClose,
|
|
140
|
-
}: {
|
|
141
|
-
chainType: ChainType;
|
|
142
|
-
onPick: (c: IXConnector) => void;
|
|
143
|
-
onBack: () => void;
|
|
144
|
-
onClose: () => void;
|
|
145
|
-
}) {
|
|
146
|
-
const connectors = sortConnectors(useXConnectors({ xChainType: chainType }), { preferred: PREFERRED });
|
|
147
|
-
return (
|
|
148
|
-
<Dialog onClose={onClose}>
|
|
149
|
-
<button type="button" onClick={onBack}>
|
|
150
|
-
← Back
|
|
151
|
-
</button>
|
|
152
|
-
<h2>Connect to {chainType}</h2>
|
|
153
|
-
{connectors.map(connector => (
|
|
154
|
-
<button type="button" key={connector.id} onClick={() => onPick(connector)} disabled={!connector.isInstalled}>
|
|
155
|
-
{connector.name}
|
|
156
|
-
{!connector.isInstalled && ' (not installed)'}
|
|
157
|
-
</button>
|
|
158
|
-
))}
|
|
159
|
-
</Dialog>
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function Dialog({ children, onClose }: { children: React.ReactNode; onClose: () => void }) {
|
|
164
|
-
return (
|
|
165
|
-
<dialog open onClose={onClose} style={{ padding: 16 }}>
|
|
166
|
-
{children}
|
|
167
|
-
</dialog>
|
|
168
|
-
);
|
|
169
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Next.js 15 App Router setup with SSR-safe hydration.
|
|
3
|
-
*
|
|
4
|
-
* This file shows three separate files merged into one for reference. In your
|
|
5
|
-
* project, split them into the matching paths:
|
|
6
|
-
*
|
|
7
|
-
* app/
|
|
8
|
-
* ├── layout.tsx ← server component (RootLayout below — make it the file's `export default`)
|
|
9
|
-
* ├── providers.tsx ← client component (Providers below — needs `'use client'` at top of file)
|
|
10
|
-
* └── page.tsx ← client component (Home below — needs `'use client'` at top of file)
|
|
11
|
-
*
|
|
12
|
-
* Key SSR points:
|
|
13
|
-
* - `EVM.ssr: true` enables wagmi cookie-based hydration.
|
|
14
|
-
* - `'use client'` on every component that calls a hook from this package.
|
|
15
|
-
* - QueryClient as a module constant — one client per app, never recreated.
|
|
16
|
-
* - SodaxWalletProvider config also a module constant — frozen on first render.
|
|
17
|
-
*
|
|
18
|
-
* NOTE: this combined file uses named exports only so it lints clean. Each of
|
|
19
|
-
* `RootLayout`, `Providers`, and `Home` should be the `export default` of its
|
|
20
|
-
* own file in your real project.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
24
|
-
import { SodaxWalletProvider, type SodaxWalletConfig, useConnectedChains } from '@sodax/wallet-sdk-react';
|
|
25
|
-
import { ChainKeys } from '@sodax/types';
|
|
26
|
-
import type { ReactNode } from 'react';
|
|
27
|
-
|
|
28
|
-
// ============================================================
|
|
29
|
-
// app/providers.tsx — client component
|
|
30
|
-
// (top of file: `'use client';`)
|
|
31
|
-
// ============================================================
|
|
32
|
-
|
|
33
|
-
const queryClient = new QueryClient({
|
|
34
|
-
defaultOptions: {
|
|
35
|
-
queries: { staleTime: 60 * 1000 },
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const walletConfig: SodaxWalletConfig = {
|
|
40
|
-
EVM: {
|
|
41
|
-
ssr: true, // ← required for Next.js — enables wagmi cookies hydration
|
|
42
|
-
chains: {
|
|
43
|
-
[ChainKeys.SONIC_MAINNET]: { rpcUrl: 'https://rpc.soniclabs.com' },
|
|
44
|
-
[ChainKeys.ETHEREUM_MAINNET]: { rpcUrl: 'https://ethereum-rpc.publicnode.com' },
|
|
45
|
-
[ChainKeys.BSC_MAINNET]: { rpcUrl: 'https://bsc-dataseed.binance.org' },
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export function Providers({ children }: { children: ReactNode }) {
|
|
51
|
-
return (
|
|
52
|
-
<QueryClientProvider client={queryClient}>
|
|
53
|
-
<SodaxWalletProvider config={walletConfig}>{children}</SodaxWalletProvider>
|
|
54
|
-
</QueryClientProvider>
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ============================================================
|
|
59
|
-
// app/layout.tsx — server component
|
|
60
|
-
// (no `'use client'`; in your real file, change `export function` to `export default function`)
|
|
61
|
-
// ============================================================
|
|
62
|
-
|
|
63
|
-
export function RootLayout({ children }: { children: ReactNode }) {
|
|
64
|
-
return (
|
|
65
|
-
<html lang="en">
|
|
66
|
-
<body>
|
|
67
|
-
<Providers>{children}</Providers>
|
|
68
|
-
</body>
|
|
69
|
-
</html>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ============================================================
|
|
74
|
-
// app/page.tsx — client component (because we use a hook)
|
|
75
|
-
// (top of file: `'use client';`; change `export function` to `export default function`)
|
|
76
|
-
// ============================================================
|
|
77
|
-
|
|
78
|
-
export function Home() {
|
|
79
|
-
// Gate UI on hydration to prevent reload flicker
|
|
80
|
-
const { chains, status } = useConnectedChains();
|
|
81
|
-
|
|
82
|
-
if (status === 'loading') {
|
|
83
|
-
return <p>Loading…</p>;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (chains.length === 0) {
|
|
87
|
-
return <p>No wallets connected</p>;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return (
|
|
91
|
-
<ul>
|
|
92
|
-
{chains.map((c) => (
|
|
93
|
-
<li key={c.chainType}>
|
|
94
|
-
{c.chainType}: <code>{c.account.address}</code>
|
|
95
|
-
</li>
|
|
96
|
-
))}
|
|
97
|
-
</ul>
|
|
98
|
-
);
|
|
99
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WalletConnect setup — adds the WalletConnect connector to the EVM modal.
|
|
3
|
-
* For dApps that target users on mobile-only or enterprise custody wallets
|
|
4
|
-
* (Ledger Live, Safe, Fireblocks, ...) which cannot install browser extensions.
|
|
5
|
-
*
|
|
6
|
-
* Get a projectId at https://cloud.walletconnect.com
|
|
7
|
-
*
|
|
8
|
-
* To narrow the WalletConnect modal to ONE specific wallet (e.g. an enterprise
|
|
9
|
-
* custody integration), see the commented `qrModalOptions` block below — fill
|
|
10
|
-
* in the target wallet id from https://walletconnect.com/explorer.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
14
|
-
import {
|
|
15
|
-
SodaxWalletProvider,
|
|
16
|
-
type SodaxWalletConfig,
|
|
17
|
-
useWalletModal,
|
|
18
|
-
useXConnectors,
|
|
19
|
-
useXAccount,
|
|
20
|
-
useXDisconnect,
|
|
21
|
-
} from '@sodax/wallet-sdk-react';
|
|
22
|
-
import { ChainKeys } from '@sodax/types';
|
|
23
|
-
|
|
24
|
-
const queryClient = new QueryClient();
|
|
25
|
-
|
|
26
|
-
const walletConfig: SodaxWalletConfig = {
|
|
27
|
-
EVM: {
|
|
28
|
-
ssr: true,
|
|
29
|
-
chains: {
|
|
30
|
-
[ChainKeys.SONIC_MAINNET]: { rpcUrl: 'https://rpc.soniclabs.com' },
|
|
31
|
-
[ChainKeys.ETHEREUM_MAINNET]: { rpcUrl: 'https://ethereum-rpc.publicnode.com' },
|
|
32
|
-
[ChainKeys.ARBITRUM_MAINNET]: { rpcUrl: 'https://arb1.arbitrum.io/rpc' },
|
|
33
|
-
},
|
|
34
|
-
walletConnect: {
|
|
35
|
-
projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID ?? '',
|
|
36
|
-
// Optional — restrict the WalletConnect QR modal to a specific wallet:
|
|
37
|
-
//
|
|
38
|
-
// qrModalOptions: {
|
|
39
|
-
// explorerRecommendedWalletIds: ['<target-wallet-id-from-walletconnect-explorer>'],
|
|
40
|
-
// explorerExcludedWalletIds: 'ALL', // hide everything except recommended
|
|
41
|
-
// },
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export function App() {
|
|
47
|
-
return (
|
|
48
|
-
<QueryClientProvider client={queryClient}>
|
|
49
|
-
<SodaxWalletProvider config={walletConfig}>
|
|
50
|
-
<ConnectWithWalletConnect />
|
|
51
|
-
</SodaxWalletProvider>
|
|
52
|
-
</QueryClientProvider>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function ConnectWithWalletConnect() {
|
|
57
|
-
const modal = useWalletModal();
|
|
58
|
-
const connectors = useXConnectors({ xChainType: 'EVM' });
|
|
59
|
-
const account = useXAccount({ xChainType: 'EVM' });
|
|
60
|
-
const disconnect = useXDisconnect();
|
|
61
|
-
|
|
62
|
-
const wcConnector = connectors.find((c) => c.id === 'walletConnect');
|
|
63
|
-
|
|
64
|
-
// Hide the modal while wagmi/WalletConnect's QR modal owns the screen
|
|
65
|
-
if (modal.state.kind === 'connecting' && modal.state.connector.id === 'walletConnect') {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (account.address) {
|
|
70
|
-
return (
|
|
71
|
-
<div>
|
|
72
|
-
<code>{account.address}</code>
|
|
73
|
-
<button type="button" onClick={() => disconnect({ xChainType: 'EVM' })}>
|
|
74
|
-
Disconnect
|
|
75
|
-
</button>
|
|
76
|
-
</div>
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (!wcConnector) {
|
|
81
|
-
return <p>WalletConnect not configured — set NEXT_PUBLIC_WC_PROJECT_ID</p>;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<button type="button" onClick={() => modal.selectWallet(wcConnector)}>
|
|
86
|
-
Connect via WalletConnect
|
|
87
|
-
</button>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# Examples
|
|
2
|
-
|
|
3
|
-
Copy-paste-runnable code examples. Each file is complete — drop it into a fresh React 19 project with `@sodax/wallet-sdk-react` + `@tanstack/react-query` installed and it works.
|
|
4
|
-
|
|
5
|
-
For narrative + step-by-step explanation, see [`../recipes/`](../recipes/). Examples here are pure code with minimal comments.
|
|
6
|
-
|
|
7
|
-
| File | Use case | Lines |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| [`01-minimal-evm.tsx`](./01-minimal-evm.tsx) | Smallest working setup — provider + 1 connect button | ~50 |
|
|
10
|
-
| [`02-multi-chain-modal.tsx`](./02-multi-chain-modal.tsx) | Headless wallet modal with chain → connector → connecting flow | ~120 |
|
|
11
|
-
| [`03-nextjs-app-router.tsx`](./03-nextjs-app-router.tsx) | Next.js 15 App Router setup (SSR + `'use client'`) | ~60 |
|
|
12
|
-
| [`04-walletconnect-setup.tsx`](./04-walletconnect-setup.tsx) | WalletConnect connector setup (with optional single-wallet filter) | ~50 |
|
|
13
|
-
|
|
14
|
-
## Conventions
|
|
15
|
-
|
|
16
|
-
- All files use TypeScript (`.tsx`).
|
|
17
|
-
- All consumer components have `'use client'` (Next.js compatible).
|
|
18
|
-
- `walletConfig` is defined as a **module-level constant** — never created inside a component (the provider freezes config on first render).
|
|
19
|
-
- `QueryClient` is also a module-level constant — one client per app, shared across providers.
|
|
20
|
-
- Examples use `<button>`, `<select>`, `<dialog>` etc. directly — bring your own UI library.
|
|
21
|
-
|
|
22
|
-
## Running an example
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
pnpm add @sodax/wallet-sdk-react @tanstack/react-query
|
|
26
|
-
# also: react@>=19, react-dom@>=19
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Drop the file into your app, mount the provider component at the root, and use the consumer component anywhere below it.
|
|
@@ -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
|
-
```
|