@flarenetwork/multichain-wallet-connector 0.0.1-rc.0
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 +690 -0
- package/dist/core/index.d.mts +2 -0
- package/dist/core/index.mjs +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +5 -0
- package/dist/multichain-C5wKiJke.mjs +2863 -0
- package/dist/multichain-C5wKiJke.mjs.map +1 -0
- package/dist/react/index.d.mts +3 -0
- package/dist/react/index.mjs +5 -0
- package/dist/use-wallets-BfbbQmDw.d.mts +311 -0
- package/dist/use-wallets-BfbbQmDw.d.mts.map +1 -0
- package/dist/use-wallets-DXRPNM7H.mjs +885 -0
- package/dist/use-wallets-DXRPNM7H.mjs.map +1 -0
- package/dist/utils-C64Z4fWz.d.mts +974 -0
- package/dist/utils-C64Z4fWz.d.mts.map +1 -0
- package/package.json +131 -0
package/README.md
ADDED
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
# @flarenetwork/multichain-wallet-connector
|
|
2
|
+
|
|
3
|
+
> **WIP** — This library is under active development. APIs are subject to change.
|
|
4
|
+
|
|
5
|
+
A TypeScript library for connecting to multiple blockchain wallets across EVM and non-EVM chains, with first-class React support.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Multi-wallet** — MetaMask, WalletConnect, Ledger, Xaman, D'CENT
|
|
10
|
+
- **Multi-chain** — EVM chains (Ethereum, Flare, Songbird) + XRP Ledger
|
|
11
|
+
- **React hooks** — Type-safe hooks for wallet state, chain clients, and connection actions
|
|
12
|
+
- **Headless UI helpers** — Wallet selection state and Ledger address selection (bring your own UI)
|
|
13
|
+
- **CAIP-2 identifiers** — Standard chain addressing (`eip155:14`, `xrpl:0`)
|
|
14
|
+
- **Discriminated unions** — Type-safe connection args per wallet type
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @flarenetwork/multichain-wallet-connector
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Prerequisites
|
|
23
|
+
|
|
24
|
+
- **Node.js 24+**
|
|
25
|
+
- **pnpm** (package manager)
|
|
26
|
+
- **React 18 or 19** (peer dependency — 18 is supported because Ledger transport libraries depend on it)
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### 1. Create a MultiChain instance
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { MultiChain } from "@flarenetwork/multichain-wallet-connector";
|
|
34
|
+
|
|
35
|
+
const multichain = new MultiChain({
|
|
36
|
+
wallets: {
|
|
37
|
+
metamask: {},
|
|
38
|
+
"wallet-connect": {
|
|
39
|
+
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
|
|
40
|
+
metadata: { name: "My dApp", url: "https://mydapp.com" },
|
|
41
|
+
},
|
|
42
|
+
ledger: {},
|
|
43
|
+
xaman: { apiKey: "YOUR_XAMAN_API_KEY" },
|
|
44
|
+
dcent: {},
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Only include wallets you need. Each wallet key is optional.
|
|
50
|
+
|
|
51
|
+
### 2. Setup React provider
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { MultichainProvider } from "@flarenetwork/multichain-wallet-connector/react";
|
|
55
|
+
|
|
56
|
+
function App() {
|
|
57
|
+
return (
|
|
58
|
+
<MultichainProvider multichain={multichain}>
|
|
59
|
+
<YourApp />
|
|
60
|
+
</MultichainProvider>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`MultichainProvider` calls `multichain.mount()` on mount, which initializes connectors and restores previous sessions.
|
|
66
|
+
|
|
67
|
+
### 3. Connect a wallet
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { useMultichain, useWallet } from "@flarenetwork/multichain-wallet-connector/react";
|
|
71
|
+
import { useMutation } from "@tanstack/react-query";
|
|
72
|
+
|
|
73
|
+
function ConnectButton() {
|
|
74
|
+
const { connect, disconnect } = useMultichain();
|
|
75
|
+
const { isConnected, state } = useWallet("metamask");
|
|
76
|
+
|
|
77
|
+
const connectMutation = useMutation({
|
|
78
|
+
mutationFn: () => connect({ wallet: "metamask", chain: "eip155:14" }),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const disconnectMutation = useMutation({
|
|
82
|
+
mutationFn: () => disconnect("metamask"),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (isConnected) {
|
|
86
|
+
return (
|
|
87
|
+
<div>
|
|
88
|
+
<p>Connected: {state.activeAddress}</p>
|
|
89
|
+
<button onClick={() => disconnectMutation.mutate()}>Disconnect</button>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<button onClick={() => connectMutation.mutate()} disabled={connectMutation.isPending}>
|
|
96
|
+
{connectMutation.isPending ? "Connecting..." : "Connect to Flare"}
|
|
97
|
+
</button>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Supported Wallets & Chains
|
|
103
|
+
|
|
104
|
+
### Wallets
|
|
105
|
+
|
|
106
|
+
| Wallet | Chains | Connection Mode |
|
|
107
|
+
| ------------- | ---------- | --------------------------------------------------------------------------- |
|
|
108
|
+
| MetaMask | EVM | Single chain |
|
|
109
|
+
| WalletConnect | EVM + XRPL | Multiple chains at once |
|
|
110
|
+
| Ledger | EVM + XRPL | Single chain |
|
|
111
|
+
| Xaman | XRPL | No chain selection needed |
|
|
112
|
+
| D'CENT | XRPL | No chain selection needed (in-app browser only, silently skipped elsewhere) |
|
|
113
|
+
|
|
114
|
+
### Chains (CAIP-2 format)
|
|
115
|
+
|
|
116
|
+
| Chain | CAIP-2 ID |
|
|
117
|
+
| ----------------------- | ----------------- |
|
|
118
|
+
| Ethereum Mainnet | `eip155:1` |
|
|
119
|
+
| Ethereum Sepolia | `eip155:11155111` |
|
|
120
|
+
| Flare Mainnet | `eip155:14` |
|
|
121
|
+
| Flare Testnet Coston | `eip155:16` |
|
|
122
|
+
| Flare Testnet Coston2 | `eip155:114` |
|
|
123
|
+
| Songbird Canary-Network | `eip155:19` |
|
|
124
|
+
| XRP Ledger Mainnet | `xrpl:0` |
|
|
125
|
+
| XRP Ledger Testnet | `xrpl:1` |
|
|
126
|
+
|
|
127
|
+
### Wallet-Chain Support Matrix
|
|
128
|
+
|
|
129
|
+
| Wallet | EVM (`eip155:*`) | XRPL (`xrpl:*`) |
|
|
130
|
+
| ------------- | :--------------: | :-------------: |
|
|
131
|
+
| MetaMask | Yes | — |
|
|
132
|
+
| WalletConnect | Yes | Yes |
|
|
133
|
+
| Ledger | Yes | Yes |
|
|
134
|
+
| Xaman | — | Yes |
|
|
135
|
+
| D'CENT | — | Yes |
|
|
136
|
+
|
|
137
|
+
## React Hooks
|
|
138
|
+
|
|
139
|
+
All hooks are imported from `@flarenetwork/multichain-wallet-connector/react`.
|
|
140
|
+
|
|
141
|
+
### `useMultichain()`
|
|
142
|
+
|
|
143
|
+
Connection actions — connect, disconnect, and low-level connector access.
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
const { connect, disconnect, disconnectAll, getConnector } = useMultichain();
|
|
147
|
+
|
|
148
|
+
// MetaMask — single EVM chain
|
|
149
|
+
await connect({ wallet: "metamask", chain: "eip155:14" });
|
|
150
|
+
|
|
151
|
+
// WalletConnect — multiple chains at once
|
|
152
|
+
await connect({ wallet: "wallet-connect", chains: ["eip155:14", "xrpl:0"] });
|
|
153
|
+
|
|
154
|
+
// Ledger — single chain with optional BIP44 options
|
|
155
|
+
await connect({ wallet: "ledger", chain: "eip155:14", options: { bip44, ledgerHDStandard: "bip44" } });
|
|
156
|
+
|
|
157
|
+
// Xaman — no chain selection
|
|
158
|
+
await connect({ wallet: "xaman" });
|
|
159
|
+
|
|
160
|
+
// D'CENT — no chain selection (detected from wallet, in-app browser only)
|
|
161
|
+
await connect({ wallet: "dcent" });
|
|
162
|
+
|
|
163
|
+
// Disconnect
|
|
164
|
+
await disconnect("metamask");
|
|
165
|
+
await disconnectAll();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `useWallet(walletType)`
|
|
169
|
+
|
|
170
|
+
Reactive wallet state and typed connector for a specific wallet.
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
const { state, connector, isConnected, isConnecting } = useWallet("metamask");
|
|
174
|
+
|
|
175
|
+
if (isConnected) {
|
|
176
|
+
console.log(state.activeAddress); // "0x..."
|
|
177
|
+
console.log(state.caip2); // "eip155:14"
|
|
178
|
+
console.log(state.availableAddresses); // [{ address, caip2, isEvm, type }]
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### `useWallets()`
|
|
183
|
+
|
|
184
|
+
All configured wallets with their current states.
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
const wallets = useWallets();
|
|
188
|
+
// [{ type: 'metamask', state: WalletState }, { type: 'wallet-connect', state: WalletState }, ...]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### `useChain(caip2, options?)`
|
|
192
|
+
|
|
193
|
+
Get the connected wallet for a specific chain. Returns a discriminated union with a type-safe client.
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
// EVM chain — client is EvmClient (viem-based)
|
|
197
|
+
const evm = useChain("eip155:14");
|
|
198
|
+
if (evm.exists) {
|
|
199
|
+
console.log(evm.address); // "0x..." — resolved, no assertion needed
|
|
200
|
+
console.log(evm.walletType); // "metamask" | "wallet-connect" | "ledger"
|
|
201
|
+
const balance = await evm.client.getBalance(evm.address);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// XRPL chain — client is XrpClient
|
|
205
|
+
const xrpl = useChain("xrpl:0");
|
|
206
|
+
if (xrpl.exists) {
|
|
207
|
+
console.log(xrpl.address); // "r..." — resolved, no assertion needed
|
|
208
|
+
await xrpl.client.sendTx({ TransactionType: "Payment" /* ... */ });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Prefer a specific wallet when multiple could serve the same chain
|
|
212
|
+
const flare = useChain("eip155:14", { wallet: "metamask" });
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### Contract Interaction (EVM)
|
|
216
|
+
|
|
217
|
+
The EVM client exposes viem's `PublicClient` and `WalletClient` for contract reads and writes.
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
import { useChain } from "@flarenetwork/multichain-wallet-connector/react";
|
|
221
|
+
import { useMutation } from "@tanstack/react-query";
|
|
222
|
+
import { erc20Abi, parseUnits } from "viem";
|
|
223
|
+
|
|
224
|
+
function TokenBalance({ chainId }: { chainId: Eip155Caip2 }) {
|
|
225
|
+
const chain = useChain(chainId, { wallet: "metamask" });
|
|
226
|
+
|
|
227
|
+
const balance = useMutation({
|
|
228
|
+
mutationFn: async () => {
|
|
229
|
+
if (!chain.exists) throw new Error("Not connected");
|
|
230
|
+
const publicClient = chain.client.getPublicClient();
|
|
231
|
+
return publicClient.readContract({
|
|
232
|
+
address: "0xTokenAddress...",
|
|
233
|
+
abi: erc20Abi,
|
|
234
|
+
functionName: "balanceOf",
|
|
235
|
+
args: ["0xAccountAddress..."],
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// ...
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
function TokenTransfer({ chainId }: { chainId: Eip155Caip2 }) {
|
|
246
|
+
const chain = useChain(chainId, { wallet: "metamask" });
|
|
247
|
+
|
|
248
|
+
const transfer = useMutation({
|
|
249
|
+
mutationFn: async () => {
|
|
250
|
+
if (!chain.exists) throw new Error("Not connected");
|
|
251
|
+
const walletClient = chain.client.getWalletClient();
|
|
252
|
+
const [account] = await walletClient.getAddresses();
|
|
253
|
+
return walletClient.writeContract({
|
|
254
|
+
account,
|
|
255
|
+
chain: null,
|
|
256
|
+
address: "0xTokenAddress...",
|
|
257
|
+
abi: erc20Abi,
|
|
258
|
+
functionName: "transfer",
|
|
259
|
+
args: ["0xRecipient...", parseUnits("10", 18)],
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// ...
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### `useChainSupport(filter?)`
|
|
269
|
+
|
|
270
|
+
Get chain configuration, optionally filtered.
|
|
271
|
+
|
|
272
|
+
```tsx
|
|
273
|
+
// All configured chains
|
|
274
|
+
const allChains = useChainSupport();
|
|
275
|
+
|
|
276
|
+
// Only EVM chains
|
|
277
|
+
const evmChains = useChainSupport({ chainType: "evm" });
|
|
278
|
+
|
|
279
|
+
// Only XRPL chains
|
|
280
|
+
const xrplChains = useChainSupport({ chainType: "xrpl" });
|
|
281
|
+
|
|
282
|
+
// Chains supported by a specific wallet
|
|
283
|
+
const ledgerChains = useChainSupport({ wallet: "ledger" });
|
|
284
|
+
|
|
285
|
+
// Each chain has: { caip2, name, nativeCurrency, rpcUrls, ... }
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### `useWalletSupport(filter?)`
|
|
289
|
+
|
|
290
|
+
Get configured wallets with their supported chains and connection mode.
|
|
291
|
+
|
|
292
|
+
```tsx
|
|
293
|
+
const wallets = useWalletSupport();
|
|
294
|
+
// [{ type: 'metamask', name: 'MetaMask', chains: ['eip155:14', ...], singleChain: true }, ...]
|
|
295
|
+
|
|
296
|
+
// Exclude specific wallets
|
|
297
|
+
const filtered = useWalletSupport({ exclude: ["ledger"] });
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### `useMultichainEvents(handlers)`
|
|
301
|
+
|
|
302
|
+
Subscribe to global wallet events. Handlers are stable — the hook manages subscriptions internally.
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
useMultichainEvents({
|
|
306
|
+
onConnect: ({ walletType, caip2, address, availableAddresses, isReconnect }) => {
|
|
307
|
+
console.log(`${walletType} connected to ${caip2} with address ${address}`);
|
|
308
|
+
},
|
|
309
|
+
onDisconnect: ({ walletType }) => {
|
|
310
|
+
console.log(`${walletType} disconnected`);
|
|
311
|
+
},
|
|
312
|
+
onChainChanged: ({ walletType, caip2 }) => {
|
|
313
|
+
console.log(`${walletType} switched to ${caip2}`);
|
|
314
|
+
},
|
|
315
|
+
onError: ({ walletType, error }) => {
|
|
316
|
+
console.error(`${walletType} error:`, error);
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## MultiChain Instance Methods
|
|
322
|
+
|
|
323
|
+
Beyond the constructor and `mount()`, the `MultiChain` instance provides imperative methods for use outside of React components (e.g., in event callbacks or initialization logic).
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
const multichain = new MultiChain({
|
|
327
|
+
/* ... */
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Check if any wallet is connected to a chain
|
|
331
|
+
if (multichain.isConnectedByCaip2("xrpl:0")) {
|
|
332
|
+
await multichain.disconnectAll();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Get all configured wallet types
|
|
336
|
+
const types = multichain.getWalletTypes(); // ["metamask", "wallet-connect", ...]
|
|
337
|
+
|
|
338
|
+
// Get a specific connector
|
|
339
|
+
const connector = multichain.getConnector("metamask");
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Utility Functions
|
|
343
|
+
|
|
344
|
+
Type-safe wrappers for common `Object` methods.
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
import { objectKeys, objectValues, objectEntries, shortenAddress } from "@flarenetwork/multichain-wallet-connector";
|
|
348
|
+
|
|
349
|
+
objectKeys({ a: 1, b: 2 }); // Array<"a" | "b">
|
|
350
|
+
objectValues({ a: 1, b: 2 }); // Array<number>
|
|
351
|
+
objectEntries({ a: 1, b: 2 }); // Array<["a" | "b", number]>
|
|
352
|
+
|
|
353
|
+
shortenAddress("0x1234567890abcdef1234567890abcdef12345678");
|
|
354
|
+
// "0x1234...5678"
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## UI Helpers (Headless)
|
|
358
|
+
|
|
359
|
+
The library provides headless state management for common UI patterns. You build the UI; the library manages the state and actions.
|
|
360
|
+
|
|
361
|
+
### Modal System
|
|
362
|
+
|
|
363
|
+
A headless selection API for wallet and chain selection.
|
|
364
|
+
The library manages selection and connect-args derivation; your app manages modal visibility and screen/view routing.
|
|
365
|
+
|
|
366
|
+
```tsx
|
|
367
|
+
import { MultichainSelectionProvider, useMultichainSelection } from "@flarenetwork/multichain-wallet-connector/react";
|
|
368
|
+
|
|
369
|
+
<MultichainSelectionProvider
|
|
370
|
+
config={{
|
|
371
|
+
mode: "multi-wallet:multi-chain",
|
|
372
|
+
wallets: {
|
|
373
|
+
metamask: "all",
|
|
374
|
+
"wallet-connect": ["eip155:14", "xrpl:0"],
|
|
375
|
+
xaman: "all",
|
|
376
|
+
},
|
|
377
|
+
}}
|
|
378
|
+
>
|
|
379
|
+
<WalletModal />
|
|
380
|
+
</MultichainSelectionProvider>;
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### Selection Modes
|
|
384
|
+
|
|
385
|
+
| Mode | Wallet Selection | Chain Selection |
|
|
386
|
+
| ---------------------------- | :--------------: | :-------------: |
|
|
387
|
+
| `single-wallet:single-chain` | One wallet | Predetermined |
|
|
388
|
+
| `single-wallet:multi-chain` | One wallet | User selects |
|
|
389
|
+
| `multi-wallet:single-chain` | User selects | Predetermined |
|
|
390
|
+
| `multi-wallet:multi-chain` | User selects | User selects |
|
|
391
|
+
|
|
392
|
+
For `*:single-chain` modes, pass `caip2` in the config to fix the chain.
|
|
393
|
+
|
|
394
|
+
#### Using `useMultichainSelection()`
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
const selection = useMultichainSelection();
|
|
398
|
+
|
|
399
|
+
const args = selection.selectWallet("metamask");
|
|
400
|
+
selection.toggleCaip2("eip155:14");
|
|
401
|
+
selection.clearWallet();
|
|
402
|
+
selection.reset();
|
|
403
|
+
|
|
404
|
+
if (args) {
|
|
405
|
+
await connect(args);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (selection.canConnect) {
|
|
409
|
+
await connect(selection.connectArgs);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
selection.selectedWalletType;
|
|
413
|
+
selection.selectedWallet;
|
|
414
|
+
selection.selectedCaip2s;
|
|
415
|
+
selection.availableCaip2s;
|
|
416
|
+
selection.wallets;
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
#### Example A: Generic Connect Modal (app-owned view)
|
|
420
|
+
|
|
421
|
+
```tsx
|
|
422
|
+
import {
|
|
423
|
+
MultichainSelectionProvider,
|
|
424
|
+
useMultichainSelection,
|
|
425
|
+
useMultichain,
|
|
426
|
+
} from "@flarenetwork/multichain-wallet-connector/react";
|
|
427
|
+
import type { ConnectWalletArgs } from "@flarenetwork/multichain-wallet-connector/react";
|
|
428
|
+
import { useMutation } from "@tanstack/react-query";
|
|
429
|
+
import { useState } from "react";
|
|
430
|
+
|
|
431
|
+
function App() {
|
|
432
|
+
return (
|
|
433
|
+
<MultichainSelectionProvider
|
|
434
|
+
config={{
|
|
435
|
+
mode: "multi-wallet:multi-chain",
|
|
436
|
+
wallets: {
|
|
437
|
+
metamask: "all",
|
|
438
|
+
"wallet-connect": "all",
|
|
439
|
+
xaman: "all",
|
|
440
|
+
dcent: "all",
|
|
441
|
+
},
|
|
442
|
+
}}
|
|
443
|
+
>
|
|
444
|
+
<WalletModal />
|
|
445
|
+
</MultichainSelectionProvider>
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function useConnectMutation() {
|
|
450
|
+
const { connect } = useMultichain();
|
|
451
|
+
return useMutation({
|
|
452
|
+
mutationFn: (args: ConnectWalletArgs) => connect(args),
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function WalletModal() {
|
|
457
|
+
const selection = useMultichainSelection();
|
|
458
|
+
const connectMutation = useConnectMutation();
|
|
459
|
+
const [open, setOpen] = useState(false);
|
|
460
|
+
|
|
461
|
+
const view = deriveView(selection);
|
|
462
|
+
|
|
463
|
+
function deriveView(selection: ReturnType<typeof useMultichainSelection>) {
|
|
464
|
+
if (!selection.selectedWalletType) return "wallet-list";
|
|
465
|
+
if (selection.selectedWallet?.isConnected) return "success";
|
|
466
|
+
|
|
467
|
+
const shouldSkipChainSelect =
|
|
468
|
+
selection.fixedCaip2 !== null ||
|
|
469
|
+
selection.selectedWalletType === "xaman" ||
|
|
470
|
+
selection.selectedWalletType === "dcent";
|
|
471
|
+
|
|
472
|
+
return shouldSkipChainSelect ? "connecting" : "chain-select";
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (!open) return <button onClick={() => setOpen(true)}>Connect Wallet</button>;
|
|
476
|
+
|
|
477
|
+
return (
|
|
478
|
+
<dialog open>
|
|
479
|
+
<button
|
|
480
|
+
onClick={() => {
|
|
481
|
+
setOpen(false);
|
|
482
|
+
selection.reset();
|
|
483
|
+
}}
|
|
484
|
+
>
|
|
485
|
+
Close
|
|
486
|
+
</button>
|
|
487
|
+
|
|
488
|
+
{view === "wallet-list" && (
|
|
489
|
+
<div>
|
|
490
|
+
<h2>Select Wallet</h2>
|
|
491
|
+
{selection.wallets.map((wallet) => (
|
|
492
|
+
<button key={wallet.type} onClick={() => selection.selectWallet(wallet.type)}>
|
|
493
|
+
{wallet.name}
|
|
494
|
+
{wallet.isConnected && " (connected)"}
|
|
495
|
+
</button>
|
|
496
|
+
))}
|
|
497
|
+
</div>
|
|
498
|
+
)}
|
|
499
|
+
|
|
500
|
+
{view === "chain-select" && selection.selectedWallet && (
|
|
501
|
+
<div>
|
|
502
|
+
<h2>Select Chain</h2>
|
|
503
|
+
<button onClick={selection.clearWallet}>Back</button>
|
|
504
|
+
|
|
505
|
+
{selection.selectedWallet.caip2s.map((caip2) => (
|
|
506
|
+
<button
|
|
507
|
+
key={caip2}
|
|
508
|
+
onClick={() => selection.toggleCaip2(caip2)}
|
|
509
|
+
style={{ fontWeight: selection.selectedCaip2s.includes(caip2) ? "bold" : "normal" }}
|
|
510
|
+
>
|
|
511
|
+
{caip2} {selection.selectedCaip2s.includes(caip2) && "(selected)"}
|
|
512
|
+
</button>
|
|
513
|
+
))}
|
|
514
|
+
|
|
515
|
+
{connectMutation.error && <p style={{ color: "red" }}>{connectMutation.error.message}</p>}
|
|
516
|
+
|
|
517
|
+
<button
|
|
518
|
+
disabled={!selection.canConnect || connectMutation.isPending}
|
|
519
|
+
onClick={() => selection.canConnect && connectMutation.mutate(selection.connectArgs)}
|
|
520
|
+
>
|
|
521
|
+
{connectMutation.isPending ? "Connecting..." : "Connect"}
|
|
522
|
+
</button>
|
|
523
|
+
</div>
|
|
524
|
+
)}
|
|
525
|
+
|
|
526
|
+
{view === "connecting" && <p>Connecting to {selection.selectedWallet?.name}...</p>}
|
|
527
|
+
{view === "success" && <p>Connected to {selection.selectedWallet?.name}!</p>}
|
|
528
|
+
</dialog>
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
#### Example B: D'CENT Deep-Link Helper
|
|
534
|
+
|
|
535
|
+
D'CENT only works inside its in-app browser. The connector initializes silently when unavailable (no errors or console warnings) — it only throws `DCENT_NOT_IN_BROWSER` when `connect()` is explicitly called. Use `isAvailable()` to check and redirect:
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
import { DcentConnector } from "@flarenetwork/multichain-wallet-connector";
|
|
539
|
+
|
|
540
|
+
if (!DcentConnector.isAvailable()) {
|
|
541
|
+
// Pass the XRPL CAIP-2 identifier: "xrpl:0" (mainnet) or "xrpl:1" (testnet)
|
|
542
|
+
window.location.assign(DcentConnector.getDynamicLink(window.location.href, "xrpl:0"));
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Ledger Address Selection
|
|
547
|
+
|
|
548
|
+
Manage BIP44 derivation paths and address pagination for Ledger hardware wallets.
|
|
549
|
+
|
|
550
|
+
```tsx
|
|
551
|
+
import { useLedgerSelection, useWallet, Bip44 } from "@flarenetwork/multichain-wallet-connector/react";
|
|
552
|
+
import type { Caip2 } from "@flarenetwork/multichain-wallet-connector/react";
|
|
553
|
+
|
|
554
|
+
function LedgerAddressPicker({ caip2 }: { caip2: Caip2 }) {
|
|
555
|
+
const { connector } = useWallet("ledger");
|
|
556
|
+
const { selection, selections } = useLedgerSelection(caip2);
|
|
557
|
+
|
|
558
|
+
// Generate paginated BIP44 paths
|
|
559
|
+
const page = Bip44.getPaginatedBip44s({
|
|
560
|
+
from: 0,
|
|
561
|
+
to: 5,
|
|
562
|
+
ledgerHDStandard: selection.hdStandard,
|
|
563
|
+
caip2,
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// Fetch addresses from the device
|
|
567
|
+
const addresses = await connector.fetchAddressesForSelection(caip2, page.paths);
|
|
568
|
+
|
|
569
|
+
// User selects an address
|
|
570
|
+
selection.selectAddress(addresses[0]);
|
|
571
|
+
|
|
572
|
+
// Switch HD standard
|
|
573
|
+
selection.setHdStandard("ledgerLive");
|
|
574
|
+
|
|
575
|
+
// Access all selections across chains
|
|
576
|
+
console.log(selections); // { 'eip155:14': { address, bip44 }, ... }
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
Options are optional — when omitted, defaults are resolved from the active Ledger session (HD standard and selected account). You can override with explicit values:
|
|
581
|
+
|
|
582
|
+
```tsx
|
|
583
|
+
const { selection } = useLedgerSelection(caip2, {
|
|
584
|
+
initialHdStandard: "ledgerLive",
|
|
585
|
+
initialSelections: { "eip155:14": someAddress },
|
|
586
|
+
});
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
#### HD Standards
|
|
590
|
+
|
|
591
|
+
| Standard | Path Pattern | Description |
|
|
592
|
+
| ------------ | ------------------------ | --------------------------------- |
|
|
593
|
+
| `bip44` | `m/44'/coin'/0'/0/index` | Classic BIP44 derivation |
|
|
594
|
+
| `ledgerLive` | `m/44'/60'/index'/0/0` | Ledger Live derivation (EVM only) |
|
|
595
|
+
|
|
596
|
+
## Error Handling
|
|
597
|
+
|
|
598
|
+
All wallet errors use the `WalletError` class with typed error codes. Each wallet has a subclass (`LedgerError`, `MetaMaskError`, `WalletConnectError`, `XamanError`, `DcentError`) that maps raw SDK errors to typed codes via `fromError()`.
|
|
599
|
+
|
|
600
|
+
`WalletErrorHelper` provides utilities for type-checking and resolving raw errors:
|
|
601
|
+
|
|
602
|
+
```tsx
|
|
603
|
+
import { WalletErrorHelper, ErrorCode } from "@flarenetwork/multichain-wallet-connector/react";
|
|
604
|
+
|
|
605
|
+
try {
|
|
606
|
+
await connect({ wallet: "metamask", chain: "eip155:14" });
|
|
607
|
+
} catch (error) {
|
|
608
|
+
if (WalletErrorHelper.isWalletError(error)) {
|
|
609
|
+
switch (error.codeKey) {
|
|
610
|
+
case "COMMON_USER_REJECTED":
|
|
611
|
+
// User rejected the connection request
|
|
612
|
+
break;
|
|
613
|
+
case "METAMASK_NOT_INSTALLED":
|
|
614
|
+
// MetaMask extension not found
|
|
615
|
+
break;
|
|
616
|
+
case "LEDGER_WRONG_APP":
|
|
617
|
+
// Wrong app open on Ledger device
|
|
618
|
+
break;
|
|
619
|
+
case "DCENT_NOT_IN_BROWSER":
|
|
620
|
+
// Attempted to connect D'CENT outside its in-app browser
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
console.log(error.code); // 4001
|
|
625
|
+
console.log(error.codeKey); // "COMMON_USER_REJECTED"
|
|
626
|
+
console.log(error.walletType); // "metamask"
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### Resolving raw errors by wallet type
|
|
632
|
+
|
|
633
|
+
When a raw (non-`WalletError`) error bubbles up from a client, use `WalletErrorHelper.from()` to map it to the correct subclass:
|
|
634
|
+
|
|
635
|
+
```tsx
|
|
636
|
+
import { WalletErrorHelper } from "@flarenetwork/multichain-wallet-connector";
|
|
637
|
+
|
|
638
|
+
// Returns the error as-is if already a WalletError,
|
|
639
|
+
// otherwise dispatches to the subclass fromError() by wallet type.
|
|
640
|
+
// Returns null if the error can't be mapped.
|
|
641
|
+
const walletError = WalletErrorHelper.from(error, walletType);
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Error Code Ranges
|
|
645
|
+
|
|
646
|
+
| Range | Category |
|
|
647
|
+
| --------- | -------------------- |
|
|
648
|
+
| 4001–4010 | Common (all wallets) |
|
|
649
|
+
| 4100–4102 | MetaMask |
|
|
650
|
+
| 4200–4205 | Ledger |
|
|
651
|
+
| 4300–4303 | WalletConnect |
|
|
652
|
+
| 4400–4403 | Xaman |
|
|
653
|
+
| 4500–4501 | D'CENT |
|
|
654
|
+
|
|
655
|
+
## Development
|
|
656
|
+
|
|
657
|
+
### Building the library
|
|
658
|
+
|
|
659
|
+
```bash
|
|
660
|
+
pnpm install
|
|
661
|
+
pnpm build # one-time build
|
|
662
|
+
pnpm build:watch # watch mode
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Type checking
|
|
666
|
+
|
|
667
|
+
```bash
|
|
668
|
+
pnpm typecheck
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
### Running the playground
|
|
672
|
+
|
|
673
|
+
The playground is a React app in `playground/` that exercises all library features.
|
|
674
|
+
|
|
675
|
+
```bash
|
|
676
|
+
pnpm dev
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
This starts the Vite dev server from `playground/`. The playground imports the library from the parent directory source, so changes to the library are reflected immediately.
|
|
680
|
+
|
|
681
|
+
### Linting
|
|
682
|
+
|
|
683
|
+
```bash
|
|
684
|
+
pnpm lint:check
|
|
685
|
+
pnpm lint:fix
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
## License
|
|
689
|
+
|
|
690
|
+
MIT
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { A as LedgerConnectOptions, B as ErrorEvent, C as LedgerSession, D as ChainClient, E as EvmClient, F as ChainChangedEvent, G as ResolvedWalletConfig, H as MetaMaskWalletOptions, I as ChainOverride, J as WalletOptions, K as WalletConnectWalletOptions, L as ConnectEvent, M as LedgerPaginatedBip44, N as Bip44, O as LedgerCaip2, P as AccountChangedEvent, R as DcentWalletOptions, S as ConnectorTypeMap, T as XrpClient, U as MultiChainEvents, V as LedgerWalletOptions, W as MultiChainOptions, X as WalletAddressState, Y as XamanWalletOptions, Z as WalletState, _ as ConfigResolver, a as shortenAddress, at as EvmChain, c as WalletConnectError, ct as XrplChain, d as DcentError, et as WalletType, f as WalletError, g as MultiChain, h as ChainGuards, i as objectValues, it as Eip155Caip2, j as LedgerHDStandard, k as LedgerComputedAddress, l as MetaMaskError, m as ErrorCodeKey, n as objectFromEntries, nt as Caip2, o as WalletErrorHelper, ot as NonEvmChain, p as ErrorCode, q as WalletMetadata, r as objectKeys, rt as Chain, s as XamanError, st as XRPlCaip2, t as objectEntries, tt as BaseChain, u as LedgerError, w as DcentConnector, x as Connector, z as DisconnectEvent } from "../utils-C64Z4fWz.mjs";
|
|
2
|
+
export { type AccountChangedEvent, type BaseChain, Bip44, type Caip2, type Chain, type ChainChangedEvent, type ChainClient, ChainGuards, type ChainOverride, ConfigResolver, type ConnectEvent, type Connector, type ConnectorTypeMap, DcentConnector, DcentError, type DcentWalletOptions, type DisconnectEvent, type Eip155Caip2, ErrorCode, type ErrorCodeKey, type ErrorEvent, type EvmChain, EvmClient, type LedgerCaip2, type LedgerComputedAddress, type LedgerConnectOptions, LedgerError, type LedgerHDStandard, type LedgerPaginatedBip44, type LedgerSession, type LedgerWalletOptions, MetaMaskError, type MetaMaskWalletOptions, MultiChain, type MultiChainEvents, type MultiChainOptions, type NonEvmChain, type ResolvedWalletConfig, type WalletAddressState, WalletConnectError, type WalletConnectWalletOptions, WalletError, WalletErrorHelper, type WalletMetadata, type WalletOptions, type WalletState, type WalletType, type XRPlCaip2, XamanError, type XamanWalletOptions, type XrpClient, type XrplChain, objectEntries, objectFromEntries, objectKeys, objectValues, shortenAddress };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { _ as objectFromEntries, a as WalletErrorHelper, b as shortenAddress, c as MetaMaskError, d as WalletError, f as ErrorCode, g as objectEntries, h as ChainGuards, i as DcentConnector, l as LedgerError, n as EvmClient, o as XamanError, p as ConfigResolver, r as Bip44, s as WalletConnectError, t as MultiChain, u as DcentError, v as objectKeys, y as objectValues } from "../multichain-C5wKiJke.mjs";
|
|
2
|
+
|
|
3
|
+
export { Bip44, ChainGuards, ConfigResolver, DcentConnector, DcentError, ErrorCode, EvmClient, LedgerError, MetaMaskError, MultiChain, WalletConnectError, WalletError, WalletErrorHelper, XamanError, objectEntries, objectFromEntries, objectKeys, objectValues, shortenAddress };
|