name: sodax-wallet-sdk-react
description: 'Add SODAX multi-chain wallet connectivity to a React dapp with @sodax/wallet-sdk-react — connect/disconnect, account & signing hooks, a headless wallet modal, WalletConnect, and bridging the connected wallet into @sodax/sdk. INTEGRATION (write NEW code) — the React wallet connectivity layer for multi-chain dapps. Covers SodaxWalletProvider setup, hook-based connect/disconnect/account/signing UX across 9 chain types (EVM, Solana, Sui, Bitcoin, Stellar, ICON, Injective, NEAR, Stacks), headless wallet-modal primitives, WalletConnect for non-injected wallets (Fireblocks/Ledger/mobile), and useWalletProvider for bridging the connected wallet into @sodax/sdk calls. Use whenever a React dapp needs SODAX wallet connectivity. Triggers on "add wallet connect", "set up SodaxWalletProvider", "useXConnect", "useXAccount", "useWalletProvider", "useWalletModal", "wallet modal", "connect button", "multi-chain wallet UI", "WalletConnect for Fireblocks". Peer deps: react >= 19, @tanstack/react-query 5.x. MIGRATION (port v1 → v2) — the v2 reshape removed useXWagmiStore from the public API (each selector must be replaced with a public hook like useXServices / useXService({ xChainType }) / useXConnections / useXConnection({ xChainType }); do NOT rename to useXWalletStore — the v2 barrel does not export it), unified hook signatures to single-object params, reshaped SodaxWalletProvider props (removed rpcConfig / options / initialState in favor of config: SodaxWalletConfig), and flattened the chain-slot wrapper (chains: { EVM, SOLANA, ... } → top-level slots on SodaxWalletConfig). Triggers on "useXWagmiStore is gone", "SodaxWalletProvider props broke", "old wallet-sdk-react hooks", "upgrade @sodax/wallet-sdk-react". Load this skill if EITHER applies; the body gates by mode.'
license: MIT
metadata:
version: '0.0.1'
author: sodax
When to use this skill
AGENTS.md routes you here when you're working with @sodax/wallet-sdk-react v2 — either writing new code or porting from v1.
Pick your mode:
- Writing NEW v2 code (greenfield, no v1 imports)? → § Integration mode below.
- Porting EXISTING v1 code to v2 (grep finds
useXWagmiStore,rpcConfig,initialState, or positional-args hooks)? → § Migration mode below. - Both? → migration first, then integration.
For backend / non-React → use sodax-wallet-sdk-core instead.
For React dapps with hooks wrapping the SDK → also load sodax-dapp-kit.
Integration mode (writing new v2 code)
Pick this mode when the consumer is a React dapp that needs SODAX wallet connectivity. Common signals:
- "I need a wallet connect button" —
useXConnect,useXAccount. - "Multi-chain modal UI" —
useWalletModal,useChainGroups,useConnectedChains. - "Pass the connected wallet to a
@sodax/sdkswap" —useWalletProvider({ xChainId }). - "Add WalletConnect for enterprise custody (Fireblocks, mobile)" —
walletConnectfield onSodaxWalletConfig.EVM. - "SSR with Next.js" —
ssr: trueflag on the EVM slot.
Workflow
- Read
integration/knowledge/ai-rules.md— DO / DON'T + workflow. - Always start with setup →
integration/knowledge/recipes/setup.md. MountSodaxWalletProvider, declare chain-type slots, wire@tanstack/react-query. - Read
integration/knowledge/architecture.md— provider mount tree, frozen config, EVM single-connection model,xChainTypevsxChainId. - Task-specific recipes →
integration/knowledge/recipes/—connect-button.md,multi-chain-modal.md,walletconnect-setup.md,bridge-to-sdk.md,sign-message.md,switch-chain.md,batch-operations.md,chain-detection.md,sub-path-imports.md. - Working examples →
integration/knowledge/examples/— 4 working.tsxapp shells (01-minimal-evm,02-multi-chain-modal,03-nextjs-app-router,04-walletconnect-setup). - Lookups →
integration/knowledge/reference/— hooks, connectors, chain-support, wallet-brands, api-surface.
Conventions to follow (integration)
- Single object parameter on every hook.
useXConnectors({ xChainType: 'EVM' }),useXAccount({ xChainId: ChainKeys.BSC_MAINNET }),useWalletProvider({ xChainType: 'EVM' }).useXAccountanduseWalletProvidertake eitherxChainId(chain key) orxChainType(family) — never both. useXConnectis a React Query mutation. Pass anIXConnectortomutate/mutateAsync. For provider-managed chains (EVM/Solana/Sui), the resolved value isundefined— read the connected account viauseXAccountafter the mutation lands.- Persisted connections. Connections survive page reloads via
localStorage(keyxwagmi-store). Gate UI on hydration withuseConnectedChains().status === 'ready'to avoid flicker. - EVM is one connection across all networks. wagmi covers every configured EVM network under one connector —
useChainGroups/useConnectedChainsreport a singleEVMrow. - Configurable chain opt-in.
SodaxWalletProvider configaccepts aSodaxWalletConfigwhere top-level keys areChainTypeslots (EVM,SOLANA,SUI,BITCOIN,STELLAR,INJECTIVE,ICON,NEAR,STACKS). Omit a slot to skip mounting that adapter; pass{}to mount with SDK defaults. - Don't import concrete chain classes from the barrel.
EvmXService,XverseXConnector, etc. are deep-import only (@sodax/wallet-sdk-react/xchains/<chain>). The barrel only exports hooks, types, interfaces, andSodaxWalletProvider.
Wallet modal primitives
Headless building blocks (render-agnostic, wallet-agnostic):
| Hook | Purpose |
|---|---|
useWalletModal({ onConnected }) |
State machine: closed → chainSelect → walletSelect → connecting → success | error. |
useConnectionFlow() |
connect + status + retry without a modal. |
useBatchConnect({ connectors, skipConnected }) |
Sequential connect across every chain a wallet identifier covers. |
useBatchDisconnect({ connectors? }) |
Mirror of useBatchConnect; omit connectors to disconnect all. |
useChainGroups({ order? }) |
One entry per enabled chain; EVM collapses to one group. |
useConnectedChains({ order? }) |
Aggregate connected view; status: 'loading' | 'ready'. |
useIsWalletInstalled({ connectors?, chainType? }) |
Cross-chain install check; filters AND. |
sortConnectors(xs, { preferred }) |
Preferred first, then installed, then original. |
Top traps to avoid (integration)
- Passing both
xChainIdandxChainTypetouseXAccount/useWalletProvider. They're mutually exclusive. - Casting the return value of
useWalletProvider. Use the chain-key narrowing pattern (passing a specificxChainId) to get the typedIXxxWalletProviderwithoutas. - Importing concrete classes from the barrel.
EvmXService,XverseXConnector, etc. live behind@sodax/wallet-sdk-react/xchains/<chain>deep imports. - Ignoring the persist-hydration gate. Render UI before
useConnectedChains().status === 'ready'and you get a flicker / stale-state. - Forgetting WalletConnect setup for enterprise custody. Default EVM discovery is EIP-6963 (browser extensions only). Fireblocks / Ledger / mobile-only wallets need the
walletConnectfield on theEVMslot.
Verification (integration)
pnpm tsc --noEmit # must exit clean
Manually verify in browser:
- Connect / disconnect flows work for at least one wallet per enabled chain.
- Page reload preserves the connection (until
useXDisconnect). useWalletProvider({ xChainId: ChainKeys.X })returns the chain-narrowedIXxxWalletProvider, ready to pass into@sodax/sdkcalls (withraw: false).
Migration mode (v1 → v2 porting)
Pick this mode if the consumer has v1 wallet-sdk-react patterns. Grep signals:
grep -rE 'useXWagmiStore|rpcConfig|initialState' src/
grep -rE 'SodaxWalletProvider.*(options|rpcConfig|initialState)' src/
The package name did not change between v1 and v2 — both versions publish as @sodax/wallet-sdk-react. Migration is detected by import surface, not by package name.
If a project has both v1 patterns AND a request for new features: migration first, then integration.
Workflow
- Read
migration-v1-to-v2/knowledge/ai-rules.md— DO / DON'T + workflow + stop conditions. - Read
migration-v1-to-v2/knowledge/breaking-changes.md— full narrative of every v1 → v2 change. - Apply per-task recipes from
migration-v1-to-v2/knowledge/recipes/—connect-button.md,multi-chain-modal.md,ssr-setup.md,walletconnect-migration.md. - Use
reference/lookups when symbols don't match —imports.md,hooks.md,config.md,components.md. - Verify with
checklist.md.
Top mechanical changes
useXWagmiStoreremoved from the public API. v2 does not export the store hook at all — neitheruseXWagmiStorenoruseXWalletStoreis available from the package barrel. Replace eachuseXWagmiStore(state => state.X)selector with the matching public hook (useXServices,useXService({ xChainType }),useXConnections,useXConnection({ xChainType }), etc.). Seemigration-v1-to-v2/knowledge/reference/imports.md§ "Store hook removed from the public API" for the full field-to-hook map and a STOP decision tree for selectors hitting v2-internal fields. The localStorage persistence key'xwagmi-store'is unchanged, so user connections survive the upgrade.- Hook args unified to a single object. v1 hooks took positional args; v2 hooks take
{ xChainType }or{ xChainId }(mutually exclusive onuseXAccount/useWalletProvider). SodaxWalletProviderprops. v1'srpcConfig,options,initialStateprops are removed. v2 takes oneconfig: SodaxWalletConfigprop where top-level keys areChainTypeslots (EVM,SOLANA,SUI, …). The oldchains: { EVM, SOLANA, ... }wrapper is also gone — chain-type slots are now top-level.- Per-chain entry shape varies. EVM/SOLANA/SUI/ICON/NEAR use
{ rpcUrl?, defaults? }; BITCOIN/STELLAR/INJECTIVE extend their*RpcConfigwith{ defaults? }; STACKS accepts a preset name orStacksNetworkLike & { defaults? }.
Stop conditions (defer to user)
| Signal | Why stop |
|---|---|
User wants a chain family not in integration/knowledge/reference/chain-support.md |
Adding a new chain is a maintainer task. |
User has a custom XService / XConnector subclass with non-trivial logic |
Custom subclasses are a maintainer-only path. Confirm scope first. |
| User mixes v1 and v2 patterns in new code being written | Do migration first, then integration. |
DO (migration)
- Replace every
useXWagmiStore(state => state.X)selector with the matching v2 public hook (useXServices,useXService({ xChainType }),useXConnections,useXConnection({ xChainType }), …) — drop theuseXWagmiStoreimport entirely. Seemigration-v1-to-v2/knowledge/reference/imports.md§ "Store hook removed" for the field-to-hook map. - Reshape
SodaxWalletProviderprops to the singleconfigobject. - Move RPC config from old
rpcConfigprop to per-chain{ rpcUrl }under the relevant slot. - For EVM-only WalletConnect setup (Fireblocks etc.), add
walletConnect: { projectId: '…' }to the EVM slot — seerecipes/walletconnect-migration.md.
DO NOT (migration)
- Rename
useXWagmiStoretouseXWalletStore. The v2 barrel does not export either name. The store implementation file is nameduseXWalletStore.tsinternally, but the hook is private — every consumer call site must move to a public hook. - Keep destructuring positional args from hooks — every v2 hook takes one object.
- Use
rpcConfig/options/initialStateprops onSodaxWalletProvider— they're gone. - Pass both
xChainIdandxChainTypetouseXAccount/useWalletProvider— mutually exclusive. - Forget about persist hydration.
useConnectedChains().status === 'ready'gates UI; render too early and you get flicker.
Verification (migration)
pnpm tsc --noEmit # must exit clean
grep -rE 'useXWagmiStore|useXWalletStore' src/ # empty — v2 exports neither
grep -rE 'SodaxWalletProvider.*(rpcConfig|initialState)' src/ # empty
Manual: connections still survive page reload (localStorage key xwagmi-store was preserved for backward compat).
Related skills
sodax-dapp-kit— React hooks wrapping@sodax/sdk. UseuseWalletProviderto bridge.sodax-sdk— for any direct SDK call from the React app (or for the SDK-level v1 → v2 work that often runs alongside this one).sodax-wallet-sdk-core— the underlying provider classes thatuseWalletProviderreturns.