@sodax/wallet-sdk-react 1.5.7-beta → 2.0.0-rc.2
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 +103 -145
- package/ai-exported/AGENTS.md +122 -0
- package/ai-exported/integration/README.md +102 -0
- package/ai-exported/integration/ai-rules.md +136 -0
- package/ai-exported/integration/architecture.md +181 -0
- package/ai-exported/integration/examples/01-minimal-evm.tsx +75 -0
- package/ai-exported/integration/examples/02-multi-chain-modal.tsx +169 -0
- package/ai-exported/integration/examples/03-nextjs-app-router.tsx +99 -0
- package/ai-exported/integration/examples/04-walletconnect-setup.tsx +89 -0
- package/ai-exported/integration/examples/README.md +29 -0
- package/ai-exported/integration/recipes/batch-operations.md +223 -0
- package/ai-exported/integration/recipes/bridge-to-sdk.md +164 -0
- package/ai-exported/integration/recipes/chain-detection.md +254 -0
- package/ai-exported/integration/recipes/connect-button.md +156 -0
- package/ai-exported/integration/recipes/multi-chain-modal.md +199 -0
- package/ai-exported/integration/recipes/setup.md +158 -0
- package/ai-exported/integration/recipes/sign-message.md +137 -0
- package/ai-exported/integration/recipes/sub-path-imports.md +95 -0
- package/ai-exported/integration/recipes/switch-chain.md +141 -0
- package/ai-exported/integration/recipes/walletconnect-setup.md +139 -0
- package/ai-exported/integration/reference/api-surface.md +175 -0
- package/ai-exported/integration/reference/chain-support.md +78 -0
- package/ai-exported/integration/reference/connectors.md +74 -0
- package/ai-exported/integration/reference/hooks.md +204 -0
- package/ai-exported/integration/reference/wallet-brands.md +106 -0
- package/ai-exported/migration/README.md +49 -0
- package/ai-exported/migration/ai-rules.md +144 -0
- package/ai-exported/migration/breaking-changes.md +305 -0
- package/ai-exported/migration/checklist.md +159 -0
- package/ai-exported/migration/recipes/connect-button.md +166 -0
- package/ai-exported/migration/recipes/multi-chain-modal.md +244 -0
- package/ai-exported/migration/recipes/ssr-setup.md +162 -0
- package/ai-exported/migration/recipes/walletconnect-migration.md +168 -0
- package/ai-exported/migration/reference/components.md +73 -0
- package/ai-exported/migration/reference/config.md +307 -0
- package/ai-exported/migration/reference/hooks.md +278 -0
- package/ai-exported/migration/reference/imports.md +157 -0
- package/dist/XConnector-B9YQTVJ4.d.ts +146 -0
- package/dist/chunk-2BOUGCJ7.mjs +150 -0
- package/dist/chunk-2BOUGCJ7.mjs.map +1 -0
- package/dist/chunk-66BAUK56.mjs +202 -0
- package/dist/chunk-66BAUK56.mjs.map +1 -0
- package/dist/chunk-7ULB6DW4.mjs +102 -0
- package/dist/chunk-7ULB6DW4.mjs.map +1 -0
- package/dist/chunk-BKJB527E.mjs +125 -0
- package/dist/chunk-BKJB527E.mjs.map +1 -0
- package/dist/chunk-BXJLBR4G.mjs +88 -0
- package/dist/chunk-BXJLBR4G.mjs.map +1 -0
- package/dist/chunk-E5IAZ7E6.mjs +186 -0
- package/dist/chunk-E5IAZ7E6.mjs.map +1 -0
- package/dist/chunk-MAQ47Q52.mjs +33 -0
- package/dist/chunk-MAQ47Q52.mjs.map +1 -0
- package/dist/chunk-MXZVF5HR.mjs +34 -0
- package/dist/chunk-MXZVF5HR.mjs.map +1 -0
- package/dist/chunk-N5A2TMF6.mjs +33 -0
- package/dist/chunk-N5A2TMF6.mjs.map +1 -0
- package/dist/chunk-NY7U7OJW.mjs +64 -0
- package/dist/chunk-NY7U7OJW.mjs.map +1 -0
- package/dist/chunk-PJLEJVAU.mjs +140 -0
- package/dist/chunk-PJLEJVAU.mjs.map +1 -0
- package/dist/chunk-PLCA4ZDJ.mjs +1585 -0
- package/dist/chunk-PLCA4ZDJ.mjs.map +1 -0
- package/dist/chunk-TZMKDXFA.mjs +3 -0
- package/dist/chunk-TZMKDXFA.mjs.map +1 -0
- package/dist/chunk-X2MHIWXO.mjs +100 -0
- package/dist/chunk-X2MHIWXO.mjs.map +1 -0
- package/dist/chunk-XZ7CHO2S.mjs +41 -0
- package/dist/chunk-XZ7CHO2S.mjs.map +1 -0
- package/dist/config-OlnzyEUE.d.ts +146 -0
- package/dist/index.cjs +2784 -1594
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +768 -1498
- package/dist/index.mjs +463 -2004
- package/dist/index.mjs.map +1 -1
- package/dist/xchains/bitcoin/index.cjs +1927 -0
- package/dist/xchains/bitcoin/index.cjs.map +1 -0
- package/dist/xchains/bitcoin/index.d.ts +125 -0
- package/dist/xchains/bitcoin/index.mjs +16 -0
- package/dist/xchains/bitcoin/index.mjs.map +1 -0
- package/dist/xchains/evm/index.cjs +316 -0
- package/dist/xchains/evm/index.cjs.map +1 -0
- package/dist/xchains/evm/index.d.ts +39 -0
- package/dist/xchains/evm/index.mjs +5 -0
- package/dist/xchains/evm/index.mjs.map +1 -0
- package/dist/xchains/icon/index.cjs +311 -0
- package/dist/xchains/icon/index.cjs.map +1 -0
- package/dist/xchains/icon/index.d.ts +37 -0
- package/dist/xchains/icon/index.mjs +7 -0
- package/dist/xchains/icon/index.mjs.map +1 -0
- package/dist/xchains/injective/index.cjs +223 -0
- package/dist/xchains/injective/index.cjs.map +1 -0
- package/dist/xchains/injective/index.d.ts +35 -0
- package/dist/xchains/injective/index.mjs +5 -0
- package/dist/xchains/injective/index.mjs.map +1 -0
- package/dist/xchains/near/index.cjs +190 -0
- package/dist/xchains/near/index.cjs.map +1 -0
- package/dist/xchains/near/index.d.ts +34 -0
- package/dist/xchains/near/index.mjs +6 -0
- package/dist/xchains/near/index.mjs.map +1 -0
- package/dist/xchains/solana/index.cjs +186 -0
- package/dist/xchains/solana/index.cjs.map +1 -0
- package/dist/xchains/solana/index.d.ts +26 -0
- package/dist/xchains/solana/index.mjs +7 -0
- package/dist/xchains/solana/index.mjs.map +1 -0
- package/dist/xchains/stacks/index.cjs +240 -0
- package/dist/xchains/stacks/index.cjs.map +1 -0
- package/dist/xchains/stacks/index.d.ts +36 -0
- package/dist/xchains/stacks/index.mjs +5 -0
- package/dist/xchains/stacks/index.mjs.map +1 -0
- package/dist/xchains/stellar/index.cjs +322 -0
- package/dist/xchains/stellar/index.cjs.map +1 -0
- package/dist/xchains/stellar/index.d.ts +44 -0
- package/dist/xchains/stellar/index.mjs +6 -0
- package/dist/xchains/stellar/index.mjs.map +1 -0
- package/dist/xchains/sui/index.cjs +248 -0
- package/dist/xchains/sui/index.cjs.map +1 -0
- package/dist/xchains/sui/index.d.ts +37 -0
- package/dist/xchains/sui/index.mjs +7 -0
- package/dist/xchains/sui/index.mjs.map +1 -0
- package/docs/ADDING_A_NEW_CHAIN.md +440 -0
- package/docs/ARCHITECTURE.md +291 -0
- package/docs/BATCH_OPERATIONS.md +267 -0
- package/docs/CHAIN_DETECTION.md +216 -0
- package/docs/CONFIGURE_PROVIDER.md +360 -0
- package/docs/CONNECTORS.md +247 -0
- package/docs/CONNECT_FLOW.md +276 -0
- package/docs/EVM_SWITCH_CHAIN.md +161 -0
- package/docs/SIGN_MESSAGE.md +213 -0
- package/docs/SUB_PATH_EXPORTS.md +246 -0
- package/docs/WALLETCONNECT.md +154 -0
- package/docs/WALLET_MODAL.md +331 -0
- package/docs/WALLET_PROVIDER_BRIDGE.md +226 -0
- package/package.json +37 -12
- package/skills/SKILLS.md +84 -0
- package/skills/bridge-to-sdk.md +148 -0
- package/skills/connect-button.md +116 -0
- package/skills/evm-only-walletconnect.md +111 -0
- package/skills/multi-chain-modal.md +178 -0
- package/skills/setup.md +107 -0
- package/dist/index.d.cts +0 -1579
- package/src/Hydrate.ts +0 -65
- package/src/SodaxWalletProvider.tsx +0 -97
- package/src/actions/getXChainType.ts +0 -8
- package/src/actions/getXService.ts +0 -33
- package/src/actions/index.ts +0 -2
- package/src/assets/wallets/hana.svg +0 -6
- package/src/assets/wallets/havah.svg +0 -76
- package/src/assets/wallets/keplr.svg +0 -30
- package/src/assets/wallets/metamask.svg +0 -60
- package/src/assets/wallets/phantom.svg +0 -4
- package/src/assets/wallets/sui.svg +0 -20
- package/src/core/XConnector.ts +0 -54
- package/src/core/XService.ts +0 -85
- package/src/core/index.ts +0 -2
- package/src/hooks/index.ts +0 -11
- package/src/hooks/useEthereumChainId.ts +0 -44
- package/src/hooks/useEvmSwitchChain.ts +0 -91
- package/src/hooks/useWalletProvider.ts +0 -206
- package/src/hooks/useXAccount.ts +0 -51
- package/src/hooks/useXAccounts.ts +0 -56
- package/src/hooks/useXBalances.ts +0 -65
- package/src/hooks/useXConnect.ts +0 -118
- package/src/hooks/useXConnection.ts +0 -72
- package/src/hooks/useXConnectors.ts +0 -72
- package/src/hooks/useXDisconnect.ts +0 -73
- package/src/hooks/useXService.ts +0 -8
- package/src/hooks/useXSignMessage.ts +0 -82
- package/src/index.ts +0 -19
- package/src/types/index.ts +0 -22
- package/src/useXWagmiStore.ts +0 -116
- package/src/utils/index.ts +0 -21
- package/src/xchains/bitcoin/BitcoinXConnector.ts +0 -34
- package/src/xchains/bitcoin/BitcoinXService.ts +0 -40
- package/src/xchains/bitcoin/OKXXConnector.ts +0 -117
- package/src/xchains/bitcoin/UnisatXConnector.ts +0 -117
- package/src/xchains/bitcoin/XverseXConnector.ts +0 -232
- package/src/xchains/bitcoin/index.ts +0 -7
- package/src/xchains/bitcoin/useBitcoinXConnectors.ts +0 -14
- package/src/xchains/evm/EvmXConnector.ts +0 -27
- package/src/xchains/evm/EvmXService.ts +0 -211
- package/src/xchains/evm/index.ts +0 -3
- package/src/xchains/icon/IconHanaXConnector.ts +0 -39
- package/src/xchains/icon/IconXService.ts +0 -117
- package/src/xchains/icon/actions.ts +0 -28
- package/src/xchains/icon/iconex/index.tsx +0 -46
- package/src/xchains/icon/index.ts +0 -2
- package/src/xchains/injective/InjectiveXConnector.ts +0 -60
- package/src/xchains/injective/InjectiveXService.ts +0 -62
- package/src/xchains/injective/actions.ts +0 -32
- package/src/xchains/injective/index.ts +0 -2
- package/src/xchains/near/NearXConnector.ts +0 -42
- package/src/xchains/near/NearXService.ts +0 -46
- package/src/xchains/near/useNearXConnectors.ts +0 -23
- package/src/xchains/solana/SolanaXConnector.ts +0 -26
- package/src/xchains/solana/SolanaXService.ts +0 -46
- package/src/xchains/solana/index.ts +0 -2
- package/src/xchains/stacks/StacksXConnector.ts +0 -63
- package/src/xchains/stacks/StacksXService.ts +0 -59
- package/src/xchains/stacks/constants.ts +0 -42
- package/src/xchains/stacks/index.ts +0 -4
- package/src/xchains/stacks/useStacksXConnectors.ts +0 -7
- package/src/xchains/stellar/CustomSorobanServer.ts +0 -93
- package/src/xchains/stellar/StellarWalletsKitXConnector.ts +0 -53
- package/src/xchains/stellar/StellarXService.ts +0 -93
- package/src/xchains/stellar/actions.ts +0 -24
- package/src/xchains/stellar/index.tsx +0 -2
- package/src/xchains/stellar/useStellarXConnectors.ts +0 -21
- package/src/xchains/stellar/utils.ts +0 -49
- package/src/xchains/sui/SuiXConnector.ts +0 -28
- package/src/xchains/sui/SuiXService.ts +0 -66
- package/src/xchains/sui/index.ts +0 -2
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# EVM Switch Chain
|
|
2
|
+
|
|
3
|
+
A single wagmi connection covers **every configured EVM network** (Sonic, Ethereum, Arbitrum, Base, BSC, Optimism, Polygon, Avalanche, HyperEVM, Lightlink, Redbelly, Kaia). The user picks a wallet once; switching the **active network** within that wallet is a separate concern handled by `useEvmSwitchChain`.
|
|
4
|
+
|
|
5
|
+
The hook also covers Injective's MetaMask wallet path — when the user connected Injective via MetaMask, the underlying ethereum chain id must be Ethereum mainnet (chain id `1`). `useEvmSwitchChain` detects the mismatch and exposes a switch action.
|
|
6
|
+
|
|
7
|
+
Source: [`useEvmSwitchChain.ts`](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/src/hooks/useEvmSwitchChain.ts), [`useEthereumChainId.ts`](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/src/hooks/useEthereumChainId.ts).
|
|
8
|
+
|
|
9
|
+
## Table of contents
|
|
10
|
+
|
|
11
|
+
1. [Why this exists](#why-this-exists)
|
|
12
|
+
2. [`useEvmSwitchChain` API](#useevmswitchchain-api)
|
|
13
|
+
3. [Standard EVM switch flow](#standard-evm-switch-flow)
|
|
14
|
+
4. [Injective MetaMask special case](#injective-metamask-special-case)
|
|
15
|
+
5. [`useEthereumChainId` — read the wagmi/MetaMask chain id](#useethereumchainid--read-the-wagmimetamask-chain-id)
|
|
16
|
+
6. [Safe to call when EVM is disabled](#safe-to-call-when-evm-is-disabled)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Why this exists
|
|
21
|
+
|
|
22
|
+
When you call `sodax.swaps.swap({ params: { srcChainKey: ChainKeys.BSC_MAINNET, ... }, walletProvider })`, the wallet provider must be **on BSC** at signing time — wagmi will reject the tx otherwise. But wagmi remembers the last network the user selected, so a user who connected via MetaMask on Ethereum and then opened your dApp's BSC swap form needs to switch first.
|
|
23
|
+
|
|
24
|
+
`useEvmSwitchChain` reads the connected EVM wallet's current chain id, compares against the chain id implied by `srcChainKey`, and exposes:
|
|
25
|
+
|
|
26
|
+
- `isWrongChain: boolean` — render a "Switch to BSC" CTA when `true`
|
|
27
|
+
- `handleSwitchChain()` — triggers wagmi's `switchChain()` (or `wallet_switchEthereumChain` for Injective MetaMask)
|
|
28
|
+
|
|
29
|
+
Without this hook you'd have to import wagmi directly and replicate the logic per-chain.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## `useEvmSwitchChain` API
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { useEvmSwitchChain } from '@sodax/wallet-sdk-react';
|
|
37
|
+
import { ChainKeys } from '@sodax/types';
|
|
38
|
+
|
|
39
|
+
const { isWrongChain, handleSwitchChain } = useEvmSwitchChain({
|
|
40
|
+
xChainId: ChainKeys.BSC_MAINNET,
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Field | Type | Behavior |
|
|
45
|
+
|-------|------|----------|
|
|
46
|
+
| `isWrongChain` | `boolean` | `true` when the wallet's active chain id doesn't match `xChainId`. Always `false` for non-EVM/Injective chain ids. |
|
|
47
|
+
| `handleSwitchChain` | `() => void` | Triggers the network switch. No-op for chain ids that aren't EVM or Injective. |
|
|
48
|
+
|
|
49
|
+
The hook never throws — `handleSwitchChain` returns synchronously and lets wagmi (or `wallet_switchEthereumChain`) own the user-rejection error path.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Standard EVM switch flow
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { useEvmSwitchChain, useWalletProvider } from '@sodax/wallet-sdk-react';
|
|
57
|
+
import { ChainKeys } from '@sodax/types';
|
|
58
|
+
|
|
59
|
+
function BscSwapButton() {
|
|
60
|
+
const { isWrongChain, handleSwitchChain } = useEvmSwitchChain({
|
|
61
|
+
xChainId: ChainKeys.BSC_MAINNET,
|
|
62
|
+
});
|
|
63
|
+
const walletProvider = useWalletProvider({ xChainId: ChainKeys.BSC_MAINNET });
|
|
64
|
+
|
|
65
|
+
if (!walletProvider) {
|
|
66
|
+
return <ConnectCta />;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (isWrongChain) {
|
|
70
|
+
return <button onClick={handleSwitchChain}>Switch to BSC</button>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return <button onClick={() => doSwap(walletProvider)}>Swap</button>;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Internally:
|
|
78
|
+
- Reads `chainId` from wagmi's `useAccount()`.
|
|
79
|
+
- Compares against `baseChainInfo[xChainId].chainId` (numeric EVM chain id resolved from the `SpokeChainKey`).
|
|
80
|
+
- `handleSwitchChain` calls wagmi's `useSwitchChain().switchChain({ chainId })`.
|
|
81
|
+
|
|
82
|
+
`useSwitchChain` opens the wallet's network-switch popup; wagmi handles the chain-config-not-added flow (most wallets prompt to add the chain if it's missing).
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Injective MetaMask special case
|
|
87
|
+
|
|
88
|
+
Injective offers MetaMask as a wallet option (in addition to Keplr, Leap). When MetaMask is the active Injective wallet, **the underlying Ethereum chain id must be mainnet (`1`)** — Injective uses MetaMask's `personal_sign` infrastructure which is bound to whatever EVM network the wallet is currently on.
|
|
89
|
+
|
|
90
|
+
`useEvmSwitchChain({ xChainId: ChainKeys.INJECTIVE_MAINNET })` detects the mismatch:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
isWrongChain =
|
|
94
|
+
xChainType === 'INJECTIVE' &&
|
|
95
|
+
injectiveXService?.walletStrategy.getWallet() === Wallet.Metamask &&
|
|
96
|
+
ethereumChainId !== 1; // mainnet.id from viem/chains
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`handleSwitchChain` for Injective calls `wallet_switchEthereumChain` directly on `window.ethereum` (not wagmi's `switchChain`, which only knows EVM chains registered in the wagmi config). It uses an EIP-1193-compliant promise + event listener pair:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
await Promise.race([
|
|
103
|
+
metamask.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x1' }] }),
|
|
104
|
+
new Promise(resolve => {
|
|
105
|
+
const handler = (chainId: string) => {
|
|
106
|
+
if (chainId === '0x1') {
|
|
107
|
+
metamask.removeListener('chainChanged', handler);
|
|
108
|
+
resolve();
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
metamask.on('chainChanged', handler);
|
|
112
|
+
}),
|
|
113
|
+
]);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The promise/event race covers both wallet behaviors — some wallets resolve `wallet_switchEthereumChain` only after the user confirms; others resolve immediately and emit `chainChanged` afterwards.
|
|
117
|
+
|
|
118
|
+
Keplr and Leap on Injective don't have this constraint — `isWrongChain` is `false` for those wallets regardless of network state.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## `useEthereumChainId` — read the wagmi/MetaMask chain id
|
|
123
|
+
|
|
124
|
+
A read-only helper that returns the **active Ethereum chain id** when MetaMask is the Injective wallet, otherwise `null`. Used internally by `useEvmSwitchChain`; expose-able for custom UIs that need to display the network state independently.
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import useEthereumChainId from '@sodax/wallet-sdk-react/hooks/useEthereumChainId';
|
|
128
|
+
|
|
129
|
+
const ethereumChainId = useEthereumChainId();
|
|
130
|
+
// number (e.g. 1 for mainnet) or null
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
It subscribes to MetaMask's `onChainIdChanged` event so the value stays fresh when the user switches networks outside the dApp. For non-MetaMask Injective wallets (Keplr, Leap) and non-Injective use cases, it returns `null`.
|
|
134
|
+
|
|
135
|
+
This hook is rarely needed in app code — `useEvmSwitchChain` already consumes it internally.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Safe to call when EVM is disabled
|
|
140
|
+
|
|
141
|
+
`useEvmSwitchChain` checks `useIsChainEnabled('EVM')` and returns a no-op result `{ isWrongChain: false, handleSwitchChain: () => {} }` when the EVM slot isn't mounted in `SodaxWalletProvider` config:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const evmEnabled = useIsChainEnabled('EVM');
|
|
145
|
+
if (!evmEnabled) return EVM_DISABLED_RESULT;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
This is **important** — calling wagmi's `useAccount` / `useSwitchChain` outside a `WagmiProvider` would throw. The early-return lets components opt-in to a "switch chain" CTA without checking EVM-enabled themselves at every call site.
|
|
149
|
+
|
|
150
|
+
The conditional hook call (early-return before `useEvmSwitchChainInner` runs) is technically a Rules-of-Hooks violation but **safe**: `evmEnabled` is derived from config, which is immutable after `SodaxWalletProvider` mounts (the config is captured once via `useRef`). The branch never changes during the component's lifetime.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Related docs
|
|
155
|
+
|
|
156
|
+
- [Configure SodaxWalletProvider](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/CONFIGURE_PROVIDER.md) — opt in / out of EVM
|
|
157
|
+
- [Wallet Provider Bridge](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/WALLET_PROVIDER_BRIDGE.md) — `useWalletProvider` returns the same provider for all EVM chain ids
|
|
158
|
+
- [Chain Detection](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/CHAIN_DETECTION.md) — EVM collapses to one row in `useChainGroups` / `useConnectedChains` for the same reason
|
|
159
|
+
- [Connect Flow](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/CONNECT_FLOW.md) — connect once; chain switching is a separate UX
|
|
160
|
+
- [wagmi `useSwitchChain` docs](https://wagmi.sh/react/api/hooks/useSwitchChain)
|
|
161
|
+
- [EIP-3326 — `wallet_switchEthereumChain`](https://eips.ethereum.org/EIPS/eip-3326)
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Sign Message
|
|
2
|
+
|
|
3
|
+
`useXSignMessage` is a single React Query mutation that delegates message signing to the connected wallet's `ChainActions.signMessage` implementation. The signature shape and encoding rules differ per chain — Bitcoin in particular auto-selects between BIP-322 and ECDSA based on the connected address type.
|
|
4
|
+
|
|
5
|
+
The hook source is [`useXSignMessage.ts`](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/src/hooks/useXSignMessage.ts); the per-chain wiring lives in [`chainRegistry.ts`](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/src/chainRegistry.ts) and the provider-managed `<Chain>Actions.tsx` files.
|
|
6
|
+
|
|
7
|
+
## Table of contents
|
|
8
|
+
|
|
9
|
+
1. [Hook API](#hook-api)
|
|
10
|
+
2. [Per-chain support matrix](#per-chain-support-matrix)
|
|
11
|
+
3. [Bitcoin — BIP-322 vs ECDSA auto-detect](#bitcoin--bip-322-vs-ecdsa-auto-detect)
|
|
12
|
+
4. [ICON not supported](#icon-not-supported)
|
|
13
|
+
5. [Provider-managed chains (EVM / Solana / Sui)](#provider-managed-chains-evm--solana--sui)
|
|
14
|
+
6. [Stellar / Injective / NEAR / Stacks](#stellar--injective--near--stacks)
|
|
15
|
+
7. [Error handling](#error-handling)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Hook API
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { useXSignMessage } from '@sodax/wallet-sdk-react';
|
|
23
|
+
|
|
24
|
+
const sign = useXSignMessage();
|
|
25
|
+
|
|
26
|
+
const signature = await sign.mutateAsync({
|
|
27
|
+
xChainType: 'EVM',
|
|
28
|
+
message: 'Sign in to MyDApp\nNonce: abc123',
|
|
29
|
+
});
|
|
30
|
+
// signature: `0x${string}` | Uint8Array | string | undefined
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Variables:
|
|
34
|
+
|
|
35
|
+
| Field | Type | Notes |
|
|
36
|
+
|-------|------|-------|
|
|
37
|
+
| `xChainType` | `ChainType` | Which chain to sign with (`'EVM'`, `'BITCOIN'`, …) |
|
|
38
|
+
| `message` | `string` | Plain UTF-8; per-chain wrappers handle encoding |
|
|
39
|
+
|
|
40
|
+
Return type is the discriminated union `\`0x${string}\` | Uint8Array | string | undefined` because each chain returns its native signature shape (hex for EVM, base64 for Stellar, base58 for Solana, etc.). Cast or branch on `xChainType` when consuming.
|
|
41
|
+
|
|
42
|
+
`undefined` is returned (not thrown) when the chain doesn't implement `signMessage` — currently only ICON. A one-time `console.warn` accompanies the `undefined`.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Per-chain support matrix
|
|
47
|
+
|
|
48
|
+
| Chain | Implementation | Signature shape |
|
|
49
|
+
|-------|----------------|-----------------|
|
|
50
|
+
| EVM | `signMessageAsync` from wagmi → personal_sign | `\`0x${string}\`` |
|
|
51
|
+
| Solana | `signMessage` from `@solana/wallet-adapter` | `Uint8Array` |
|
|
52
|
+
| Sui | `signPersonalMessage` from `@mysten/dapp-kit` | `string` (base64 signature + bytes) |
|
|
53
|
+
| Bitcoin | Auto-detect: BIP-322 (P2WPKH/P2TR) or ECDSA (P2SH/P2PKH) | `string` |
|
|
54
|
+
| Stellar | `walletsKit.signMessage` from `@creit.tech/stellar-wallets-kit` | `string` (base64) |
|
|
55
|
+
| Injective | `walletStrategy.signArbitrary` from `@injectivelabs/wallet-base` | `string` |
|
|
56
|
+
| NEAR | NEAR connector's `signMessage` | `string` |
|
|
57
|
+
| Stacks | `signMessage` from `@stacks/connect` | `string` |
|
|
58
|
+
| **ICON** | **Not supported** — Hana wallet does not expose a signing API | `undefined` |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Bitcoin — BIP-322 vs ECDSA auto-detect
|
|
63
|
+
|
|
64
|
+
Bitcoin's signing flow inspects the connected address and picks the right method automatically:
|
|
65
|
+
|
|
66
|
+
| Address type | Signing method | Connectors that support it |
|
|
67
|
+
|--------------|----------------|----------------------------|
|
|
68
|
+
| P2WPKH (native segwit, `bc1q…`) | BIP-322 | Unisat, Xverse, OKX |
|
|
69
|
+
| P2TR (taproot, `bc1p…`) | BIP-322 | Unisat, Xverse, OKX |
|
|
70
|
+
| P2SH (legacy multi-sig, `3…`) | ECDSA | Unisat, Xverse, OKX |
|
|
71
|
+
| P2PKH (legacy, `1…`) | ECDSA | Unisat, Xverse, OKX |
|
|
72
|
+
|
|
73
|
+
The dispatch happens inside [`chainRegistry.ts`](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/src/chainRegistry.ts) using `detectBitcoinAddressType(address)` + the `hasSignBip322` / `hasSignEcdsa` type guards from [`bitcoinSignGuards.ts`](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/src/xchains/bitcoin/bitcoinSignGuards.ts):
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
switch (addressType) {
|
|
77
|
+
case 'P2WPKH':
|
|
78
|
+
case 'P2TR':
|
|
79
|
+
if (!hasSignBip322(connector)) {
|
|
80
|
+
throw new Error(`${connector.id} does not support BIP-322 signing`);
|
|
81
|
+
}
|
|
82
|
+
return connector.signBip322Message(message);
|
|
83
|
+
|
|
84
|
+
case 'P2SH':
|
|
85
|
+
case 'P2PKH':
|
|
86
|
+
if (!hasSignEcdsa(connector)) {
|
|
87
|
+
throw new Error(`${connector.id} does not support ECDSA signing`);
|
|
88
|
+
}
|
|
89
|
+
return connector.signEcdsaMessage(message);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The same logic mirrors the SDK's `BitcoinSpokeProvider.authenticateWithWallet` — the React layer doesn't reinvent the dispatch. If a custom connector implements only one of the two methods, calling `signMessage` from a wrongly-typed address surfaces the error inline.
|
|
94
|
+
|
|
95
|
+
**Why BIP-322 for segwit/taproot?** Legacy ECDSA message signing (`signEcdsaMessage`) doesn't have a standard for non-P2PKH addresses. BIP-322 added a generic verification framework that works across address types — most modern Bitcoin wallets implement it for segwit/taproot specifically.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## ICON not supported
|
|
100
|
+
|
|
101
|
+
Hana wallet on ICON exposes account / transaction APIs but no general-purpose `signMessage` endpoint. `useXSignMessage({ xChainType: 'ICON' })` returns `undefined` and logs:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
[useXSignMessage] signMessage not supported for chain "ICON"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If you need a signature on ICON for SIWE-style auth, fall back to a transaction-based proof or skip ICON in your auth flow. The chainRegistry comment explicitly documents this: `// ICON: signMessage not implemented — Hana wallet does not expose a signing API.`
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Provider-managed chains (EVM / Solana / Sui)
|
|
112
|
+
|
|
113
|
+
For EVM, Solana, and Sui, `signMessage` is registered by the chain's `<Chain>Actions.tsx` component using a **ref to the native SDK hook**. The ref is updated on each render so the registered closure always calls the latest function:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// EvmActions.tsx — pattern shared with SolanaActions, SuiActions
|
|
117
|
+
const { signMessageAsync } = useSignMessage(); // wagmi
|
|
118
|
+
const signMessageRef = useRef(signMessageAsync);
|
|
119
|
+
useEffect(() => { signMessageRef.current = signMessageAsync; }, [signMessageAsync]);
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
registerActions({
|
|
123
|
+
signMessage: async (message) => signMessageRef.current({ message }),
|
|
124
|
+
});
|
|
125
|
+
}, []);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This pattern keeps the registered action stable (registered once on mount) while still calling the current native SDK function — important because re-registering on every render would trigger downstream re-subscribes.
|
|
129
|
+
|
|
130
|
+
Consumer perspective is the same — call `useXSignMessage` and let the layer handle the routing.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Stellar / Injective / NEAR / Stacks
|
|
135
|
+
|
|
136
|
+
Non-provider chains register `signMessage` directly in `chainRegistry`:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Stellar
|
|
140
|
+
signMessage: async (message: string) => {
|
|
141
|
+
const res = await service.walletsKit.signMessage(message);
|
|
142
|
+
return res.signedMessage;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Injective — auto-converts injective1… address to 0x… for MetaMask wallet
|
|
146
|
+
signMessage: async (message: string) => {
|
|
147
|
+
const ethereumAddress = getEthereumAddress(address);
|
|
148
|
+
return await service.walletStrategy.signArbitrary(
|
|
149
|
+
service.walletStrategy.getWallet() === Wallet.Metamask ? ethereumAddress : address,
|
|
150
|
+
message,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Each delegates to the chain's native signing API. Errors propagate as-is — you'll see `Wallet.signMessage rejected by user`, `Address mismatch`, etc., depending on the chain's SDK.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Error handling
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { useXSignMessage } from '@sodax/wallet-sdk-react';
|
|
163
|
+
|
|
164
|
+
function SignButton() {
|
|
165
|
+
const sign = useXSignMessage();
|
|
166
|
+
|
|
167
|
+
const handleSign = async () => {
|
|
168
|
+
try {
|
|
169
|
+
const signature = await sign.mutateAsync({
|
|
170
|
+
xChainType: 'EVM',
|
|
171
|
+
message: 'Sign in',
|
|
172
|
+
});
|
|
173
|
+
if (!signature) {
|
|
174
|
+
// ICON or other unsupported chain
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
submitSignature(signature);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
// User rejection, wallet disconnect mid-sign, address mismatch, etc.
|
|
180
|
+
console.error('sign failed:', error);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<button onClick={handleSign} disabled={sign.isPending}>
|
|
186
|
+
{sign.isPending ? 'Waiting for wallet…' : 'Sign'}
|
|
187
|
+
</button>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Common error messages by chain:
|
|
193
|
+
|
|
194
|
+
| Chain | Typical error |
|
|
195
|
+
|-------|---------------|
|
|
196
|
+
| EVM | `User rejected the request` (MetaMask), `User denied message signature` (Rabby) |
|
|
197
|
+
| Solana | `WalletSignMessageError: User rejected the request` |
|
|
198
|
+
| Sui | `User rejected the signature request` |
|
|
199
|
+
| Bitcoin | `<connector.id> does not support BIP-322 signing` (mismatch with address type), `User canceled the request` |
|
|
200
|
+
| Stellar | `Stellar signature not found` |
|
|
201
|
+
| Injective | `Injective signature not found`, `Injective address not found` |
|
|
202
|
+
|
|
203
|
+
`mutation.error` reflects the latest failure; `mutation.isError` / `mutation.isPending` follow standard React Query semantics.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Related docs
|
|
208
|
+
|
|
209
|
+
- [Connect Flow](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/CONNECT_FLOW.md) — must connect a wallet before signing
|
|
210
|
+
- [Configure SodaxWalletProvider](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/CONFIGURE_PROVIDER.md) — chain must be enabled in config to dispatch
|
|
211
|
+
- [Connectors](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/CONNECTORS.md) — Bitcoin connector classes for `instanceof` checks
|
|
212
|
+
- [SDK Wallet Providers Reference](https://github.com/icon-project/sodax-sdks/blob/main/packages/sdk/docs/WALLET_PROVIDERS.md) — Bitcoin's lower-level `signTransaction` / `signEcdsaMessage` / `signBip322Message` interface
|
|
213
|
+
- [BIP-322 specification](https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki) — generic signed-message format for all Bitcoin address types
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Sub-path Exports
|
|
2
|
+
|
|
3
|
+
`@sodax/wallet-sdk-react` ships a **two-tier export surface**:
|
|
4
|
+
|
|
5
|
+
- **Barrel** — `import { useXConnect, type IXConnector } from '@sodax/wallet-sdk-react'` exposes hooks, types, the abstract `XConnector` base, and `<SodaxWalletProvider>`.
|
|
6
|
+
- **Sub-path** — `import { XverseXConnector } from '@sodax/wallet-sdk-react/xchains/bitcoin'` exposes concrete connector / service classes for advanced use (`instanceof` checks, calling chain-specific methods).
|
|
7
|
+
|
|
8
|
+
The split is deliberate: keeping concrete classes off the barrel prevents accidental coupling to internal implementations and lets the SDK refactor chain code without breaking consumers.
|
|
9
|
+
|
|
10
|
+
This document covers the build-time plumbing that makes sub-paths work.
|
|
11
|
+
|
|
12
|
+
## Table of contents
|
|
13
|
+
|
|
14
|
+
1. [Why two tiers](#why-two-tiers)
|
|
15
|
+
2. [What's exported where](#whats-exported-where)
|
|
16
|
+
3. [`tsup` multi-entry build](#tsup-multi-entry-build)
|
|
17
|
+
4. [`package.json` `exports` + `typesVersions`](#packagejson-exports--typesversions)
|
|
18
|
+
5. [`instanceof` semantics across entry points](#instanceof-semantics-across-entry-points)
|
|
19
|
+
6. [Common errors and fixes](#common-errors-and-fixes)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Why two tiers
|
|
24
|
+
|
|
25
|
+
Three reasons:
|
|
26
|
+
|
|
27
|
+
**1. Coupling control.** A consumer that imports `EvmXConnector` from the barrel implicitly depends on a concrete class. Six months later when the SDK refactors how EVM connectors are constructed (e.g. moves from one connector class per wagmi connector to a polymorphic dispatcher), every consumer that did the deep dependency breaks. Forcing `instanceof` users to deep-import makes the dependency explicit and visible to the SDK author at refactor time.
|
|
28
|
+
|
|
29
|
+
**2. Tree-shaking efficiency.** The barrel re-exports hooks and types only — small surface area, small bundle. Consumers who never touch `XverseXConnector` don't pay its cost. Sub-path imports pull only the chain they reference.
|
|
30
|
+
|
|
31
|
+
**3. AI agent / IDE intent signal.** Code reviewers and autocomplete see at a glance whether a file is using "everyday hooks" (barrel) or "chain-specific advanced features" (deep import). The latter typically warrants a closer look.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## What's exported where
|
|
36
|
+
|
|
37
|
+
### Barrel (`@sodax/wallet-sdk-react`)
|
|
38
|
+
|
|
39
|
+
Everything in `src/index.ts` — hooks, utils, types, interfaces, the `<SodaxWalletProvider>` component, and the abstract `XConnector` / `XService` base classes:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// ✅ Barrel imports
|
|
43
|
+
import {
|
|
44
|
+
SodaxWalletProvider,
|
|
45
|
+
useXConnect,
|
|
46
|
+
useXAccount,
|
|
47
|
+
useWalletProvider,
|
|
48
|
+
useWalletModal,
|
|
49
|
+
useBatchConnect,
|
|
50
|
+
XConnector, // abstract base — for custom connectors
|
|
51
|
+
type IXConnector, // interface
|
|
52
|
+
type IXService, // interface
|
|
53
|
+
type SodaxWalletConfig,
|
|
54
|
+
type XAccount,
|
|
55
|
+
type XConnection,
|
|
56
|
+
sortConnectors,
|
|
57
|
+
} from '@sodax/wallet-sdk-react';
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Sub-paths (`@sodax/wallet-sdk-react/xchains/<chain>`)
|
|
61
|
+
|
|
62
|
+
Concrete classes — one barrel per chain folder:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// ✅ Sub-path imports
|
|
66
|
+
import { EvmXConnector, EvmXService, createWagmiConfig } from '@sodax/wallet-sdk-react/xchains/evm';
|
|
67
|
+
import { SolanaXConnector, SolanaXService } from '@sodax/wallet-sdk-react/xchains/solana';
|
|
68
|
+
import { SuiXConnector, SuiXService } from '@sodax/wallet-sdk-react/xchains/sui';
|
|
69
|
+
import {
|
|
70
|
+
BitcoinXService,
|
|
71
|
+
BitcoinXConnector, // abstract
|
|
72
|
+
UnisatXConnector, // concrete
|
|
73
|
+
XverseXConnector, // concrete
|
|
74
|
+
OKXXConnector, // concrete
|
|
75
|
+
useBitcoinXConnectors,
|
|
76
|
+
type BtcWalletAddressType,
|
|
77
|
+
} from '@sodax/wallet-sdk-react/xchains/bitcoin';
|
|
78
|
+
import { StellarXService, StellarWalletsKitXConnector } from '@sodax/wallet-sdk-react/xchains/stellar';
|
|
79
|
+
import { InjectiveXConnector, InjectiveXService } from '@sodax/wallet-sdk-react/xchains/injective';
|
|
80
|
+
import { IconXService, IconHanaXConnector, CHAIN_INFO, SupportedChainId } from '@sodax/wallet-sdk-react/xchains/icon';
|
|
81
|
+
import { NearXConnector, NearXService } from '@sodax/wallet-sdk-react/xchains/near';
|
|
82
|
+
import {
|
|
83
|
+
StacksXService,
|
|
84
|
+
StacksXConnector,
|
|
85
|
+
STACKS_PROVIDERS,
|
|
86
|
+
useStacksXConnectors,
|
|
87
|
+
type StacksProviderConfig,
|
|
88
|
+
} from '@sodax/wallet-sdk-react/xchains/stacks';
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
For the full list, see [`CONNECTORS.md`](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/CONNECTORS.md#sub-path-imports--concrete-classes).
|
|
92
|
+
|
|
93
|
+
### Concrete chain symbols live only in sub-paths
|
|
94
|
+
|
|
95
|
+
Concrete connector / service classes — and their named types — are not re-exported from the barrel. Even a `type`-only reference must come from the sub-path; `import type { XverseXConnector } from '@sodax/wallet-sdk-react'` fails with TS2305 / TS2724.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// ✅ Sub-path — works for both `type` and runtime use
|
|
99
|
+
import { XverseXConnector } from '@sodax/wallet-sdk-react/xchains/bitcoin';
|
|
100
|
+
import type { BtcWalletAddressType } from '@sodax/wallet-sdk-react/xchains/bitcoin';
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If a cross-cutting type genuinely needs to be available from the barrel (typing a function param, narrowing a return type), add it as `export type { ... }` in `src/index.ts` — but the default is sub-path-only.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## `tsup` multi-entry build
|
|
108
|
+
|
|
109
|
+
[`tsup.config.ts`](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/tsup.config.ts) declares one entry per public boundary:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const sharedConfig = {
|
|
113
|
+
entry: [
|
|
114
|
+
'src/index.ts',
|
|
115
|
+
'src/xchains/*/index.ts', // glob — picks up new chain folders automatically
|
|
116
|
+
'src/xchains/*/index.tsx',
|
|
117
|
+
],
|
|
118
|
+
outDir: 'dist',
|
|
119
|
+
external: ['react', 'react-dom', '@tanstack/react-query'],
|
|
120
|
+
// ...
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export default defineConfig([
|
|
124
|
+
{ ...sharedConfig, format: ['esm'], splitting: true, outExtension: () => ({ js: '.mjs' }) },
|
|
125
|
+
{ ...sharedConfig, format: ['cjs'], splitting: false, outExtension: () => ({ js: '.cjs' }) },
|
|
126
|
+
]);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Two production builds:
|
|
130
|
+
|
|
131
|
+
| Build | `splitting` | Output |
|
|
132
|
+
|-------|-------------|--------|
|
|
133
|
+
| ESM | `true` | `.mjs` files share class instances across entry points |
|
|
134
|
+
| CJS | `false` | One `.cjs` per entry, no chunk-sharing |
|
|
135
|
+
|
|
136
|
+
**Why ESM splitting matters** — without it, the same class would be defined twice (once in `dist/index.mjs`, once in `dist/xchains/bitcoin/index.mjs`), and `instanceof XverseXConnector` would return `false` if the connector was constructed in one entry and tested in another. With splitting on, both entries import the class from a shared chunk, preserving identity.
|
|
137
|
+
|
|
138
|
+
### Adding a new chain
|
|
139
|
+
|
|
140
|
+
The glob `src/xchains/*/index.ts` is **auto-discovering** — creating `src/xchains/aptos/index.ts` automatically produces `dist/xchains/aptos/index.{mjs,cjs,d.ts}` on next build. No `tsup.config.ts` edit required. See [`ADDING_A_NEW_CHAIN.md`](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/ADDING_A_NEW_CHAIN.md#step-4--xchainschainindexts-barrel-for-sub-path-export).
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## `package.json` `exports` + `typesVersions`
|
|
145
|
+
|
|
146
|
+
Two fields work together to support modern bundlers and legacy `moduleResolution: "node"`:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"exports": {
|
|
151
|
+
".": {
|
|
152
|
+
"types": "./dist/index.d.ts",
|
|
153
|
+
"import": "./dist/index.mjs",
|
|
154
|
+
"require": "./dist/index.cjs"
|
|
155
|
+
},
|
|
156
|
+
"./xchains/*": {
|
|
157
|
+
"types": "./dist/xchains/*/index.d.ts",
|
|
158
|
+
"import": "./dist/xchains/*/index.mjs",
|
|
159
|
+
"require": "./dist/xchains/*/index.cjs"
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"typesVersions": {
|
|
163
|
+
"*": {
|
|
164
|
+
"xchains/*": ["./dist/xchains/*/index.d.ts"]
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
| Field | Read by | Purpose |
|
|
171
|
+
|-------|---------|---------|
|
|
172
|
+
| `exports['.']` | All bundlers (Vite, Webpack, esbuild, Next.js), modern Node | Resolves `@sodax/wallet-sdk-react` to the right artifact per condition (ESM/CJS/types) |
|
|
173
|
+
| `exports['./xchains/*']` | Same | Wildcard maps `@sodax/wallet-sdk-react/xchains/<chain>` to the matching `dist/xchains/<chain>/index.<ext>` |
|
|
174
|
+
| `typesVersions['xchains/*']` | TypeScript with `moduleResolution: "node"` (legacy) | Fallback for TS configs that don't honor the `exports` field's `types` condition |
|
|
175
|
+
|
|
176
|
+
Modern projects with `"moduleResolution": "bundler"` or `"node16"` only need `exports`. `typesVersions` ensures sub-path types resolve for older configs that still see `@sodax/wallet-sdk-react/xchains/bitcoin` as a path traversal.
|
|
177
|
+
|
|
178
|
+
If a consumer reports "Cannot find module" for sub-paths in TypeScript, check their `tsconfig.json` `moduleResolution` setting first.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## `instanceof` semantics across entry points
|
|
183
|
+
|
|
184
|
+
Class identity is preserved across barrel and sub-path entries **only in ESM**:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
// ESM — works
|
|
188
|
+
import { XverseXConnector } from '@sodax/wallet-sdk-react/xchains/bitcoin';
|
|
189
|
+
const connectors = useXConnectors({ xChainType: 'BITCOIN' });
|
|
190
|
+
const xverse = connectors.find(c => c instanceof XverseXConnector); // ✅ may be true
|
|
191
|
+
|
|
192
|
+
// CJS — broken (in theory)
|
|
193
|
+
// require('@sodax/wallet-sdk-react') and require('@sodax/wallet-sdk-react/xchains/bitcoin')
|
|
194
|
+
// load XverseXConnector from separate compiled files; instanceof returns false.
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
In practice this **doesn't break browser consumers** — Vite, Next.js, esbuild, Webpack all resolve ESM by default. CJS-only Node.js scripts that mix barrel and sub-path imports would hit the issue, but those scripts typically don't need `instanceof XverseXConnector` (they wouldn't run in a browser-extension context).
|
|
198
|
+
|
|
199
|
+
If you're a Node.js consumer running CJS and need cross-entry `instanceof`, either:
|
|
200
|
+
- Switch your project to ESM (`"type": "module"` in your `package.json`), or
|
|
201
|
+
- Stick to **one** entry point — only the sub-path or only the barrel, never both for the same class.
|
|
202
|
+
|
|
203
|
+
The `tsup.config.ts` comment documents this trade-off:
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// CJS does not support code splitting — instanceof across barrel and sub-path
|
|
207
|
+
// entries will fail. In practice this is not an issue because browser apps (Vite,
|
|
208
|
+
// Next.js) resolve ESM, and Node.js scripts don't use sub-path instanceof checks.
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Common errors and fixes
|
|
214
|
+
|
|
215
|
+
### `Cannot find module '@sodax/wallet-sdk-react/xchains/bitcoin'`
|
|
216
|
+
|
|
217
|
+
- **TypeScript**: check `moduleResolution` is `"bundler"`, `"node16"`, or `"nodenext"`. Older `"node"` setting needs `typesVersions` (already provided in this package).
|
|
218
|
+
- **Bundler**: check `package.json` is being read — some monorepos with custom resolvers ignore `exports`. Verify by adding a console.log of `require.resolve('@sodax/wallet-sdk-react/xchains/bitcoin')`.
|
|
219
|
+
|
|
220
|
+
### `XverseXConnector is not exported from '@sodax/wallet-sdk-react'`
|
|
221
|
+
|
|
222
|
+
Expected — concrete connector classes are NOT re-exported from the barrel. Switch to:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import { XverseXConnector } from '@sodax/wallet-sdk-react/xchains/bitcoin';
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The error message can also appear if you tried `import type { XverseXConnector } from '@sodax/wallet-sdk-react'` and then used it as a runtime value — `export type` only covers type position.
|
|
229
|
+
|
|
230
|
+
### `instanceof XverseXConnector` returns `false` for a connector that should match
|
|
231
|
+
|
|
232
|
+
Likely a CJS / dual-import issue. Verify that the connector instance came from the same entry point you're testing against. Switching to ESM resolution fixes this for all bundlers.
|
|
233
|
+
|
|
234
|
+
### `Module not found: '@sodax/wallet-sdk-react/xchains/aptos'` after adding a new chain
|
|
235
|
+
|
|
236
|
+
Run `pnpm build:packages` — `dist/xchains/aptos/` only exists after a build. The `exports` wildcard resolves at runtime, so consumer apps need the freshly-built artifact.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Related docs
|
|
241
|
+
|
|
242
|
+
- [Connectors](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/CONNECTORS.md) — full sub-path map per chain
|
|
243
|
+
- [Adding a New Chain](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/ADDING_A_NEW_CHAIN.md) — Step 4 covers the barrel that powers a new sub-path
|
|
244
|
+
- [Architecture](https://github.com/icon-project/sodax-sdks/blob/main/packages/wallet-sdk-react/docs/ARCHITECTURE.md) — store-first hooks consume only barrel exports
|
|
245
|
+
- [tsup splitting docs](https://tsup.egoist.dev/#code-splitting) — official docs on the ESM splitting behavior
|
|
246
|
+
- [Node.js subpath exports](https://nodejs.org/api/packages.html#subpath-exports) — the spec behind `package.json` `exports`
|