@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 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 };