@lydianpay/lydianconnect 1.2.0 → 1.3.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/dist/index.cjs CHANGED
@@ -72,6 +72,14 @@ var ConnectManager = class {
72
72
  get(walletId) {
73
73
  return this.connections.get(walletId);
74
74
  }
75
+ /**
76
+ * Fire a wallet's deep link. Goes straight to the fallback: it is the only
77
+ * connector that owns a link table, and the only one that emits the
78
+ * `display_uri` a host is responding to when it calls this.
79
+ */
80
+ openWallet(input) {
81
+ return this.fallback.openWallet?.(input) ?? false;
82
+ }
75
83
  async disconnect(walletId) {
76
84
  if (walletId) {
77
85
  await this.connections.get(walletId)?.disconnect().catch(() => {
@@ -79,7 +87,9 @@ var ConnectManager = class {
79
87
  this.connections.delete(walletId);
80
88
  return;
81
89
  }
82
- await Promise.allSettled([...this.connections.values()].map((c) => c.disconnect()));
90
+ await Promise.allSettled(
91
+ [...this.connections.values()].map((c) => c.disconnect())
92
+ );
83
93
  this.connections.clear();
84
94
  }
85
95
  async reset() {
@@ -111,7 +121,11 @@ var NAMESPACE_DEFAULTS = {
111
121
  events: ["chainChanged", "accountsChanged"]
112
122
  },
113
123
  solana: {
114
- methods: ["solana_signTransaction", "solana_signMessage", "solana_signAndSendTransaction"],
124
+ methods: [
125
+ "solana_signTransaction",
126
+ "solana_signMessage",
127
+ "solana_signAndSendTransaction"
128
+ ],
115
129
  events: []
116
130
  },
117
131
  bip122: {
@@ -129,11 +143,17 @@ function buildNamespace(chainId) {
129
143
  );
130
144
  }
131
145
  if (name === "eip155" && !(/^\d+$/.test(reference) && Number(reference) > 0)) {
132
- throw new Error(`Invalid eip155 chain "${caip}" \u2014 the reference must be a positive integer.`);
146
+ throw new Error(
147
+ `Invalid eip155 chain "${caip}" \u2014 the reference must be a positive integer.`
148
+ );
133
149
  }
134
150
  return {
135
151
  name,
136
- value: { chains: [caip], methods: defaults.methods, events: defaults.events }
152
+ value: {
153
+ chains: [caip],
154
+ methods: defaults.methods,
155
+ events: defaults.events
156
+ }
137
157
  };
138
158
  }
139
159
 
@@ -232,10 +252,15 @@ var WALLET_CATALOG = WALLETS.map((w) => ({
232
252
  }));
233
253
 
234
254
  // src/core/connector.ts
255
+ function requestedChain(req) {
256
+ return req.namespace.value.chains?.[0];
257
+ }
235
258
  var BaseConnector = class {
236
259
  constructor() {
237
260
  this.walletIds = [];
238
261
  this.handlers = /* @__PURE__ */ new Set();
262
+ /** Depth of in-flight connects; see {@link duringConnect}. */
263
+ this.connecting = 0;
239
264
  }
240
265
  servesNamespace(_namespaceName) {
241
266
  return true;
@@ -249,7 +274,28 @@ var BaseConnector = class {
249
274
  this.handlers.add(handler);
250
275
  return () => this.handlers.delete(handler);
251
276
  }
277
+ /**
278
+ * Run a connect with its handshake events suppressed.
279
+ *
280
+ * A connector that binds provider listeners before requesting accounts sees the
281
+ * wallet's own setup traffic — an initial `chainChanged`, and another when
282
+ * `ensureChain` switches. Forwarding those makes the host believe the user moved
283
+ * networks mid-connect. Nothing is lost by dropping them: connect() reads the
284
+ * authoritative account and chain when it settles, and only changes made after
285
+ * that are the user's.
286
+ */
287
+ async duringConnect(run) {
288
+ this.connecting++;
289
+ try {
290
+ return await run();
291
+ } finally {
292
+ this.connecting--;
293
+ }
294
+ }
252
295
  emit(event) {
296
+ if (this.connecting > 0 && (event.type === "chainChanged" || event.type === "accountsChanged")) {
297
+ return;
298
+ }
253
299
  for (const handler of this.handlers) handler(event);
254
300
  }
255
301
  };
@@ -259,6 +305,80 @@ function deriveUrl() {
259
305
  return typeof window !== "undefined" ? window.location.origin : "";
260
306
  }
261
307
 
308
+ // src/connectors/eip155/chain.ts
309
+ var CHAIN_SETTLE = { timeoutMs: 4e3, intervalMs: 100 };
310
+ function toCaip(chainId) {
311
+ if (!chainId) return "eip155:1";
312
+ const num = chainId.startsWith("0x") ? parseInt(chainId, 16) : Number(chainId);
313
+ return `eip155:${Number.isFinite(num) ? num : 1}`;
314
+ }
315
+ async function currentChain(rpc2) {
316
+ const raw = await rpc2({ method: "eth_chainId" }).catch(() => null);
317
+ return toCaip(typeof raw === "string" ? raw : null);
318
+ }
319
+ async function settleChain({
320
+ rpc: rpc2,
321
+ chainId,
322
+ timeoutMs,
323
+ intervalMs
324
+ }) {
325
+ const deadline = Date.now() + timeoutMs;
326
+ for (; ; ) {
327
+ const actual = await currentChain(rpc2);
328
+ if (actual === chainId || Date.now() >= deadline) return;
329
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
330
+ }
331
+ }
332
+ async function ensureChain({
333
+ rpc: rpc2,
334
+ chainId,
335
+ timeoutMs = CHAIN_SETTLE.timeoutMs,
336
+ intervalMs = CHAIN_SETTLE.intervalMs
337
+ }) {
338
+ const chainNumber = chainId ? Number(chainId.split(":")[1]) : NaN;
339
+ if (!Number.isFinite(chainNumber) || chainNumber <= 0) return;
340
+ const requested = `eip155:${chainNumber}`;
341
+ if (await currentChain(rpc2) === requested) return;
342
+ const switched = await rpc2({
343
+ method: "wallet_switchEthereumChain",
344
+ params: [{ chainId: `0x${chainNumber.toString(16)}` }]
345
+ }).then(
346
+ () => true,
347
+ // Chain not added to the wallet, or the user declined — the caller's
348
+ // chainChanged will reflect reality. No settle: the chain isn't moving.
349
+ () => false
350
+ );
351
+ if (switched) {
352
+ await settleChain({ rpc: rpc2, chainId: requested, timeoutMs, intervalMs });
353
+ }
354
+ }
355
+
356
+ // src/core/abort.ts
357
+ function withSignal(promise, signal, onAbort) {
358
+ if (!signal) return promise;
359
+ if (signal.aborted) {
360
+ onAbort?.();
361
+ return Promise.reject(new DOMException("Aborted", "AbortError"));
362
+ }
363
+ return new Promise((resolve, reject) => {
364
+ const abortHandler = () => {
365
+ onAbort?.();
366
+ reject(new DOMException("Aborted", "AbortError"));
367
+ };
368
+ signal.addEventListener("abort", abortHandler, { once: true });
369
+ promise.then(
370
+ (value) => {
371
+ signal.removeEventListener("abort", abortHandler);
372
+ resolve(value);
373
+ },
374
+ (error) => {
375
+ signal.removeEventListener("abort", abortHandler);
376
+ reject(error);
377
+ }
378
+ );
379
+ });
380
+ }
381
+
262
382
  // src/connectors/metamask/connector.ts
263
383
  var WALLET_ID = "metamask";
264
384
  var MetaMaskConnector = class extends BaseConnector {
@@ -298,23 +418,41 @@ var MetaMaskConnector = class extends BaseConnector {
298
418
  if (this.listenersBound) return;
299
419
  this.listenersBound = true;
300
420
  provider.on("chainChanged", (hex) => {
301
- this.emit({ type: "chainChanged", walletId: WALLET_ID, chainId: toCaip(String(hex)) });
421
+ this.emit({
422
+ type: "chainChanged",
423
+ walletId: WALLET_ID,
424
+ chainId: toCaip(String(hex))
425
+ });
302
426
  });
303
427
  provider.on("accountsChanged", (accounts) => {
304
428
  const account = Array.isArray(accounts) ? accounts[0] : void 0;
305
- if (account) this.emit({ type: "accountsChanged", walletId: WALLET_ID, account });
429
+ if (account)
430
+ this.emit({ type: "accountsChanged", walletId: WALLET_ID, account });
306
431
  else this.emit({ type: "disconnect", walletId: WALLET_ID });
307
432
  });
308
- provider.on("disconnect", () => this.emit({ type: "disconnect", walletId: WALLET_ID }));
433
+ provider.on(
434
+ "disconnect",
435
+ () => this.emit({ type: "disconnect", walletId: WALLET_ID })
436
+ );
309
437
  }
310
438
  async connect(req) {
311
- const provider = await this.getProvider();
312
- const accounts = await provider.request({ method: "eth_requestAccounts" });
313
- const account = accounts?.[0];
314
- if (!account) throw new Error("MetaMask returned no account");
315
- await this.ensureChain(provider, req);
316
- const chainId = toCaip(await provider.request({ method: "eth_chainId" }));
317
- return this.toConnection(provider, account, chainId);
439
+ return this.duringConnect(async () => {
440
+ const provider = await this.getProvider();
441
+ const accounts = await withSignal(
442
+ provider.request({ method: "eth_requestAccounts" }),
443
+ req.signal
444
+ );
445
+ const account = accounts?.[0];
446
+ if (!account) throw new Error("MetaMask returned no account");
447
+ await ensureChain({
448
+ rpc: rpcFor(provider),
449
+ chainId: requestedChain(req)
450
+ });
451
+ const chainId = toCaip(
452
+ await provider.request({ method: "eth_chainId" })
453
+ );
454
+ return this.toConnection(provider, account, chainId);
455
+ });
318
456
  }
319
457
  async restore() {
320
458
  const provider = await this.getProvider().catch(() => null);
@@ -332,28 +470,15 @@ var MetaMaskConnector = class extends BaseConnector {
332
470
  this.provider = null;
333
471
  this.listenersBound = false;
334
472
  }
335
- /** Best-effort switch to the requested chain; leaves the wallet as-is if it's unknown to it. */
336
- async ensureChain(provider, req) {
337
- const wanted = req.namespace.value.chains?.[0];
338
- const wantedNum = wanted ? Number(wanted.split(":")[1]) : NaN;
339
- if (!Number.isFinite(wantedNum) || wantedNum <= 0) return;
340
- const current = parseInt(
341
- await provider.request({ method: "eth_chainId" }).catch(() => "0x0") ?? "0x0",
342
- 16
343
- );
344
- if (current === wantedNum) return;
345
- await provider.request({
346
- method: "wallet_switchEthereumChain",
347
- params: [{ chainId: `0x${wantedNum.toString(16)}` }]
348
- }).catch(() => {
349
- });
350
- }
351
473
  toConnection(provider, account, chainId) {
352
474
  return {
353
475
  walletId: WALLET_ID,
354
476
  account,
355
477
  chainId,
356
- request: async (args) => await provider.request({ method: args.method, params: args.params }),
478
+ request: async (args) => await provider.request({
479
+ method: args.method,
480
+ params: args.params
481
+ }),
357
482
  openWallet: () => {
358
483
  },
359
484
  // MetaMask SDK foregrounds the wallet on request
@@ -366,17 +491,18 @@ var MetaMaskConnector = class extends BaseConnector {
366
491
  };
367
492
  }
368
493
  };
369
- function toCaip(hexChainId) {
370
- if (!hexChainId) return "eip155:1";
371
- const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
372
- return `eip155:${Number.isFinite(num) ? num : 1}`;
494
+ function rpcFor(provider) {
495
+ return (args) => provider.request(args);
373
496
  }
374
497
 
375
498
  // src/connectors/coinbase/connector.ts
499
+ var WALLET_ID2 = "coinbase";
376
500
  function rpc(provider, method, params) {
377
- return provider.request({ method, params });
501
+ return provider.request({
502
+ method,
503
+ params
504
+ });
378
505
  }
379
- var WALLET_ID2 = "coinbase";
380
506
  var CoinbaseConnector = class extends BaseConnector {
381
507
  constructor(app) {
382
508
  super();
@@ -410,31 +536,48 @@ var CoinbaseConnector = class extends BaseConnector {
410
536
  if (this.listenersBound) return;
411
537
  this.listenersBound = true;
412
538
  provider.on("chainChanged", (hex) => {
413
- this.emit({ type: "chainChanged", walletId: WALLET_ID2, chainId: toCaip2(String(hex)) });
539
+ this.emit({
540
+ type: "chainChanged",
541
+ walletId: WALLET_ID2,
542
+ chainId: toCaip(String(hex))
543
+ });
414
544
  });
415
545
  provider.on("accountsChanged", (accounts) => {
416
546
  const account = Array.isArray(accounts) ? accounts[0] : void 0;
417
- if (account) this.emit({ type: "accountsChanged", walletId: WALLET_ID2, account });
547
+ if (account)
548
+ this.emit({ type: "accountsChanged", walletId: WALLET_ID2, account });
418
549
  else this.emit({ type: "disconnect", walletId: WALLET_ID2 });
419
550
  });
420
- provider.on("disconnect", () => this.emit({ type: "disconnect", walletId: WALLET_ID2 }));
551
+ provider.on(
552
+ "disconnect",
553
+ () => this.emit({ type: "disconnect", walletId: WALLET_ID2 })
554
+ );
421
555
  }
422
556
  async connect(req) {
423
- const provider = await this.getProvider(evmChainIds(req));
424
- const accounts = await rpc(provider, "eth_requestAccounts");
425
- const account = accounts?.[0];
426
- if (!account) throw new Error("Coinbase Wallet returned no account");
427
- await this.ensureChain(provider, req);
428
- const chainId = toCaip2(await rpc(provider, "eth_chainId"));
429
- return this.toConnection(provider, account, chainId);
557
+ return this.duringConnect(async () => {
558
+ const provider = await this.getProvider(evmChainIds(req));
559
+ const accounts = await rpc(provider, "eth_requestAccounts");
560
+ const account = accounts?.[0];
561
+ if (!account) throw new Error("Coinbase Wallet returned no account");
562
+ await ensureChain({
563
+ rpc: rpcFor2(provider),
564
+ chainId: requestedChain(req)
565
+ });
566
+ const chainId = toCaip(await rpc(provider, "eth_chainId"));
567
+ return this.toConnection(provider, account, chainId);
568
+ });
430
569
  }
431
570
  async restore() {
432
571
  const provider = await this.getProvider().catch(() => null);
433
572
  if (!provider) return [];
434
- const accounts = await rpc(provider, "eth_accounts").catch(() => []);
573
+ const accounts = await rpc(provider, "eth_accounts").catch(
574
+ () => []
575
+ );
435
576
  const account = accounts?.[0];
436
577
  if (!account) return [];
437
- const chainId = toCaip2(await rpc(provider, "eth_chainId").catch(() => "0x1"));
578
+ const chainId = toCaip(
579
+ await rpc(provider, "eth_chainId").catch(() => "0x1")
580
+ );
438
581
  return [this.toConnection(provider, account, chainId)];
439
582
  }
440
583
  async reset() {
@@ -443,20 +586,6 @@ var CoinbaseConnector = class extends BaseConnector {
443
586
  this.provider = null;
444
587
  this.listenersBound = false;
445
588
  }
446
- async ensureChain(provider, req) {
447
- const wanted = req.namespace.value.chains?.[0];
448
- const wantedNum = wanted ? Number(wanted.split(":")[1]) : NaN;
449
- if (!Number.isFinite(wantedNum) || wantedNum <= 0) return;
450
- const current = parseInt(
451
- await rpc(provider, "eth_chainId").catch(() => "0x0") ?? "0x0",
452
- 16
453
- );
454
- if (current === wantedNum) return;
455
- await rpc(provider, "wallet_switchEthereumChain", [{ chainId: `0x${wantedNum.toString(16)}` }]).catch(
456
- () => {
457
- }
458
- );
459
- }
460
589
  toConnection(provider, account, chainId) {
461
590
  return {
462
591
  walletId: WALLET_ID2,
@@ -476,10 +605,8 @@ var CoinbaseConnector = class extends BaseConnector {
476
605
  };
477
606
  }
478
607
  };
479
- function toCaip2(hexChainId) {
480
- if (!hexChainId) return "eip155:1";
481
- const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
482
- return `eip155:${Number.isFinite(num) ? num : 1}`;
608
+ function rpcFor2(provider) {
609
+ return ({ method, params }) => rpc(provider, method, params);
483
610
  }
484
611
  function evmChainIds(req) {
485
612
  const wanted = req.namespace.value.chains?.[0];
@@ -513,7 +640,8 @@ var InjectedConnector = class extends BaseConnector {
513
640
  this.listening = true;
514
641
  window.addEventListener("eip6963:announceProvider", (event) => {
515
642
  const detail = event.detail;
516
- if (detail?.info?.rdns && detail.provider) this.discovered.set(detail.info.rdns, detail);
643
+ if (detail?.info?.rdns && detail.provider)
644
+ this.discovered.set(detail.info.rdns, detail);
517
645
  });
518
646
  window.dispatchEvent(new Event("eip6963:requestProvider"));
519
647
  }
@@ -532,16 +660,20 @@ var InjectedConnector = class extends BaseConnector {
532
660
  }
533
661
  async connect(req) {
534
662
  const provider = this.providerFor(req.walletId);
535
- if (!provider) throw new Error(`No injected provider announced for "${req.walletId}"`);
663
+ if (!provider)
664
+ throw new Error(`No injected provider announced for "${req.walletId}"`);
536
665
  const accounts = await withSignal(
537
666
  provider.request({ method: "eth_requestAccounts" }),
538
667
  // triggers the extension popup
539
668
  req.signal
540
669
  );
541
670
  const account = accounts?.[0];
542
- if (!account) throw new Error(`${req.walletId} extension returned no account`);
543
- await this.ensureChain(provider, req);
544
- const chainId = toCaip3(await provider.request({ method: "eth_chainId" }));
671
+ if (!account)
672
+ throw new Error(`${req.walletId} extension returned no account`);
673
+ await ensureChain({ rpc: rpcFor3(provider), chainId: requestedChain(req) });
674
+ const chainId = toCaip(
675
+ await provider.request({ method: "eth_chainId" })
676
+ );
545
677
  this.bindEvents(provider, req.walletId);
546
678
  return this.toConnection(provider, req.walletId, account, chainId);
547
679
  }
@@ -558,7 +690,7 @@ var InjectedConnector = class extends BaseConnector {
558
690
  const accounts = await provider.request({ method: "eth_accounts" }).catch(() => []);
559
691
  const account = accounts?.[0];
560
692
  if (!account) continue;
561
- const chainId = toCaip3(
693
+ const chainId = toCaip(
562
694
  await provider.request({ method: "eth_chainId" }).catch(() => "0x1")
563
695
  );
564
696
  this.bindEvents(provider, walletId);
@@ -574,133 +706,480 @@ var InjectedConnector = class extends BaseConnector {
574
706
  if (this.bound.has(provider)) return;
575
707
  this.bound.add(provider);
576
708
  provider.on("chainChanged", (hex) => {
577
- this.emit({ type: "chainChanged", walletId, chainId: toCaip3(String(hex)) });
709
+ this.emit({
710
+ type: "chainChanged",
711
+ walletId,
712
+ chainId: toCaip(String(hex))
713
+ });
578
714
  });
579
715
  provider.on("accountsChanged", (accounts) => {
580
716
  const account = Array.isArray(accounts) ? accounts[0] : void 0;
581
717
  if (account) this.emit({ type: "accountsChanged", walletId, account });
582
718
  else this.emit({ type: "disconnect", walletId });
583
719
  });
584
- provider.on("disconnect", () => this.emit({ type: "disconnect", walletId }));
585
- }
586
- /** Best-effort switch to the requested chain; leaves the wallet as-is if it's unknown to it. */
587
- async ensureChain(provider, req) {
588
- const wanted = req.namespace.value.chains?.[0];
589
- const wantedNum = wanted ? Number(wanted.split(":")[1]) : NaN;
590
- if (!Number.isFinite(wantedNum) || wantedNum <= 0) return;
591
- const current = parseInt(
592
- await provider.request({ method: "eth_chainId" }).catch(() => "0x0") ?? "0x0",
593
- 16
720
+ provider.on(
721
+ "disconnect",
722
+ () => this.emit({ type: "disconnect", walletId })
594
723
  );
595
- if (current === wantedNum) return;
596
- await provider.request({
597
- method: "wallet_switchEthereumChain",
598
- params: [{ chainId: `0x${wantedNum.toString(16)}` }]
599
- }).catch(() => {
600
- });
601
724
  }
602
725
  toConnection(provider, walletId, account, chainId) {
603
726
  return {
604
727
  walletId,
605
728
  account,
606
729
  chainId,
607
- request: async (args) => await provider.request({ method: args.method, params: args.params }),
730
+ request: async (args) => await provider.request({
731
+ method: args.method,
732
+ params: args.params
733
+ }),
608
734
  openWallet: () => {
609
735
  },
610
736
  // the extension foregrounds its own popup on request
611
737
  disconnect: async () => {
612
- await provider.request({ method: "wallet_revokePermissions", params: [{ eth_accounts: {} }] }).catch(() => {
738
+ await provider.request({
739
+ method: "wallet_revokePermissions",
740
+ params: [{ eth_accounts: {} }]
741
+ }).catch(() => {
613
742
  });
614
743
  this.emit({ type: "disconnect", walletId });
615
744
  }
616
745
  };
617
746
  }
618
747
  };
619
- function toCaip3(hexChainId) {
620
- if (!hexChainId) return "eip155:1";
621
- const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
622
- return `eip155:${Number.isFinite(num) ? num : 1}`;
623
- }
624
- function withSignal(promise, signal) {
625
- if (!signal) return promise;
626
- if (signal.aborted) return Promise.reject(new DOMException("Aborted", "AbortError"));
627
- return new Promise((resolve, reject) => {
628
- const onAbort = () => reject(new DOMException("Aborted", "AbortError"));
629
- signal.addEventListener("abort", onAbort, { once: true });
630
- promise.then(
631
- (value) => {
632
- signal.removeEventListener("abort", onAbort);
633
- resolve(value);
634
- },
635
- (error) => {
636
- signal.removeEventListener("abort", onAbort);
637
- reject(error);
638
- }
639
- );
640
- });
748
+ function rpcFor3(provider) {
749
+ return (args) => provider.request(args);
641
750
  }
642
751
 
643
752
  // src/connectors/walletconnect/wallet-links.ts
644
753
  var WALLET_LINKS = {
645
- metamask: { "native": "metamask://", "scheme": "metamask://wc", "universal": "https://metamask.app.link/wc", "store": { "ios": "https://apps.apple.com/us/app/metamask/id1438144202", "android": "https://play.google.com/store/apps/details?id=io.metamask" } },
646
- trust: { "native": "trust://", "scheme": "trust://wc", "universal": "https://link.trustwallet.com/wc", "store": { "ios": "https://apps.apple.com/app/apple-store/id1288339409", "android": "https://play.google.com/store/apps/details?id=com.wallet.crypto.trustapp" } },
647
- rainbow: { "native": "rainbow://", "scheme": "rainbow://wc", "universal": "https://rnbwapp.com/wc", "store": { "ios": "https://apps.apple.com/app/apple-store/id1457119021?pt=119997837&ct=wc&mt=8", "android": "https://play.google.com/store/apps/details?id=me.rainbow&referrer=utm_source%3Dwc%26utm_medium%3Dconnector%26utm_campaign%3Dwc" } },
648
- coinbase: { "store": { "ios": "https://apps.apple.com/us/app/base-formerly-coinbase-wallet/id1278383455" } },
649
- zerion: { "native": "zerion://", "scheme": "zerion://wc", "universal": "https://wallet.zerion.io/wc/wc", "store": { "ios": "https://apps.apple.com/app/id1456732565", "android": "https://play.google.com/store/apps/details?id=io.zerion.android&hl=en&gl=US" } },
650
- tokenpocket: { "native": "tpoutside://", "scheme": "tpoutside://wc", "store": { "ios": "https://apps.apple.com/us/app/tp-wallet/id6444625622?l=en", "android": "https://play.google.com/store/apps/details?id=vip.mytokenpocket" } },
651
- blockchaincom: { "native": "blockchain-wallet://", "scheme": "blockchain-wallet://wc", "universal": "https://login.blockchain.com/app/wc", "store": { "ios": "https://apps.apple.com/us/app/blockchain-bitcoin-wallet/id493253309", "android": "https://play.google.com/store/apps/details?id=piuk.blockchain.android" } },
652
- exodus: { "native": "exodus://", "scheme": "exodus://wc", "universal": "https://exodus.com/m/wc", "store": { "ios": "https://apps.apple.com/us/app/exodus-crypto-bitcoin-wallet/id1414384820", "android": "https://play.google.com/store/apps/details?id=exodusmovement.exodus&hl=en&gl=US" } },
653
- phantom: { "store": { "ios": "https://apps.apple.com/us/app/phantom-crypto-wallet/id1598432977", "android": "https://play.google.com/store/apps/details?id=app.phantom&hl=en" } },
654
- okx: { "native": "okxwallet://main", "scheme": "okxwallet://main/wc", "store": { "ios": "https://apps.apple.com/us/app/okx-buy-bitcoin-eth-crypto/id1327268470", "android": "https://play.google.com/store/apps/details?id=com.okinc.okex.gp" } },
655
- cryptocom: { "native": "cryptowallet://", "scheme": "cryptowallet://wc", "store": { "ios": "https://apps.apple.com/US/app/id1512048310?mt=8", "android": "https://play.google.com/store/apps/details?id=com.defi.wallet" } },
656
- argent: { "native": "argent://app/", "scheme": "argent://app/wc", "universal": "https://www.argent.xyz/app/wc", "store": { "ios": "https://apps.apple.com/us/app/argent-defi-in-a-tap/id1358741926", "android": "https://play.google.com/store/apps/details?id=im.argent.contractwalletclient&hl=en&gl=US&pli=1" } },
657
- safepal: { "native": "safepalwallet://", "scheme": "safepalwallet://wc", "universal": "https://link.safepal.io/wc", "store": { "ios": "https://apps.apple.com/app/safepal-wallet/id1548297139", "android": "https://play.google.com/store/apps/details?id=io.safepal.wallet" } },
658
- imtoken: { "native": "imtokenv2://", "scheme": "imtokenv2://wc", "store": { "ios": "https://apps.apple.com/us/app/imtoken2/id1384798940", "android": "https://play.google.com/store/apps/details?id=im.token.app" } },
659
- ronin: { "native": "roninwallet://", "scheme": "roninwallet://wc", "universal": "https://wallet.roninchain.com/wc", "store": { "ios": "https://apps.apple.com/us/app/ronin-wallet/id1592675001", "android": "https://play.google.com/store/apps/details?id=com.skymavis.genesis" } },
660
- coin98: { "native": "coin98://", "scheme": "coin98://wc", "universal": "https://coin98.com/wc", "store": { "ios": "https://apps.apple.com/vn/app/coin98-wallet/id1561969966", "android": "https://play.google.com/store/apps/details?id=coin98.crypto.finance.media&hl=vi&gl=US" } },
661
- bitkeep: { "native": "bitkeep://", "scheme": "bitkeep://wc", "universal": "https://bkapp.vip/wc", "store": { "ios": "https://web3.bitget.com/en/wallet-download?type=0", "android": "https://web3.bitget.com/en/wallet-download?type=0" } },
662
- "1inch": { "native": "oneinch://open/nobodywilleveruseit", "scheme": "oneinch://open/nobodywilleveruseit/wc", "universal": "https://wallet.1inch.io/app/nobodywilleveruseit/wc", "store": { "ios": "https://apps.apple.com/us/app/1inch-defi-wallet/id1546049391", "android": "https://play.google.com/store/apps/details?id=io.oneinch.android" } },
663
- ledgerlive: { "native": "ledgerlive://", "scheme": "ledgerlive://wc", "store": { "ios": "https://itunes.apple.com/app/id1361671700", "android": "https://play.google.com/store/apps/details?id=com.ledger.live" } },
664
- atomic: { "native": "atomicwallet://", "scheme": "atomicwallet://wc", "store": { "ios": "https://apps.apple.com/us/app/atomic-wallet/id1478257827", "android": "https://play.google.com/store/apps/details?id=io.atomicwallet" } },
665
- alpha: { "native": "awallet://", "scheme": "awallet://wc", "universal": "https://aw.app/wc", "store": { "ios": "https://apps.apple.com/us/app/alphawallet-eth-wallet/id1358230430", "android": "https://play.google.com/store/apps/details?id=io.stormbird.wallet" } },
666
- math: { "native": "mathwallet://", "scheme": "mathwallet://wc", "universal": "https://www.mathwallet.org/wc", "store": { "ios": "https://apps.apple.com/us/app/mathwallet5/id1582612388", "android": "https://play.google.com/store/apps/details?id=com.mathwallet.android" } },
667
- bitpay: { "native": "bitpay://", "scheme": "bitpay://wc", "universal": "https://link.bitpay.com/wc", "store": { "ios": "https://bitpay.onelink.me/Cenw/ejjaw7bs", "android": "https://bitpay.onelink.me/Cenw/ejjaw7bs" } },
668
- rabby: { "native": "rabby://", "scheme": "rabby://wc", "store": { "ios": "https://apps.apple.com/us/app/rabby-wallet-crypto-evm/id6474381673", "android": "https://play.google.com/store/apps/details?id=com.debank.rabbymobile" } },
754
+ metamask: {
755
+ native: "metamask://",
756
+ scheme: "metamask://wc",
757
+ universal: "https://metamask.app.link/wc",
758
+ store: {
759
+ ios: "https://apps.apple.com/us/app/metamask/id1438144202",
760
+ android: "https://play.google.com/store/apps/details?id=io.metamask"
761
+ }
762
+ },
763
+ trust: {
764
+ native: "trust://",
765
+ scheme: "trust://wc",
766
+ universal: "https://link.trustwallet.com/wc",
767
+ store: {
768
+ ios: "https://apps.apple.com/app/apple-store/id1288339409",
769
+ android: "https://play.google.com/store/apps/details?id=com.wallet.crypto.trustapp"
770
+ }
771
+ },
772
+ rainbow: {
773
+ native: "rainbow://",
774
+ scheme: "rainbow://wc",
775
+ universal: "https://rnbwapp.com/wc",
776
+ store: {
777
+ ios: "https://apps.apple.com/app/apple-store/id1457119021?pt=119997837&ct=wc&mt=8",
778
+ android: "https://play.google.com/store/apps/details?id=me.rainbow&referrer=utm_source%3Dwc%26utm_medium%3Dconnector%26utm_campaign%3Dwc"
779
+ }
780
+ },
781
+ coinbase: {
782
+ store: {
783
+ ios: "https://apps.apple.com/us/app/base-formerly-coinbase-wallet/id1278383455"
784
+ }
785
+ },
786
+ zerion: {
787
+ native: "zerion://",
788
+ scheme: "zerion://wc",
789
+ universal: "https://wallet.zerion.io/wc/wc",
790
+ store: {
791
+ ios: "https://apps.apple.com/app/id1456732565",
792
+ android: "https://play.google.com/store/apps/details?id=io.zerion.android&hl=en&gl=US"
793
+ }
794
+ },
795
+ tokenpocket: {
796
+ native: "tpoutside://",
797
+ scheme: "tpoutside://wc",
798
+ store: {
799
+ ios: "https://apps.apple.com/us/app/tp-wallet/id6444625622?l=en",
800
+ android: "https://play.google.com/store/apps/details?id=vip.mytokenpocket"
801
+ }
802
+ },
803
+ blockchaincom: {
804
+ native: "blockchain-wallet://",
805
+ scheme: "blockchain-wallet://wc",
806
+ universal: "https://login.blockchain.com/app/wc",
807
+ store: {
808
+ ios: "https://apps.apple.com/us/app/blockchain-bitcoin-wallet/id493253309",
809
+ android: "https://play.google.com/store/apps/details?id=piuk.blockchain.android"
810
+ }
811
+ },
812
+ exodus: {
813
+ native: "exodus://",
814
+ scheme: "exodus://wc",
815
+ universal: "https://exodus.com/m/wc",
816
+ store: {
817
+ ios: "https://apps.apple.com/us/app/exodus-crypto-bitcoin-wallet/id1414384820",
818
+ android: "https://play.google.com/store/apps/details?id=exodusmovement.exodus&hl=en&gl=US"
819
+ }
820
+ },
821
+ phantom: {
822
+ store: {
823
+ ios: "https://apps.apple.com/us/app/phantom-crypto-wallet/id1598432977",
824
+ android: "https://play.google.com/store/apps/details?id=app.phantom&hl=en"
825
+ }
826
+ },
827
+ okx: {
828
+ native: "okxwallet://main",
829
+ scheme: "okxwallet://main/wc",
830
+ store: {
831
+ ios: "https://apps.apple.com/us/app/okx-buy-bitcoin-eth-crypto/id1327268470",
832
+ android: "https://play.google.com/store/apps/details?id=com.okinc.okex.gp"
833
+ }
834
+ },
835
+ cryptocom: {
836
+ native: "cryptowallet://",
837
+ scheme: "cryptowallet://wc",
838
+ store: {
839
+ ios: "https://apps.apple.com/US/app/id1512048310?mt=8",
840
+ android: "https://play.google.com/store/apps/details?id=com.defi.wallet"
841
+ }
842
+ },
843
+ argent: {
844
+ native: "argent://app/",
845
+ scheme: "argent://app/wc",
846
+ universal: "https://www.argent.xyz/app/wc",
847
+ store: {
848
+ ios: "https://apps.apple.com/us/app/argent-defi-in-a-tap/id1358741926",
849
+ android: "https://play.google.com/store/apps/details?id=im.argent.contractwalletclient&hl=en&gl=US&pli=1"
850
+ }
851
+ },
852
+ safepal: {
853
+ native: "safepalwallet://",
854
+ scheme: "safepalwallet://wc",
855
+ universal: "https://link.safepal.io/wc",
856
+ store: {
857
+ ios: "https://apps.apple.com/app/safepal-wallet/id1548297139",
858
+ android: "https://play.google.com/store/apps/details?id=io.safepal.wallet"
859
+ }
860
+ },
861
+ imtoken: {
862
+ native: "imtokenv2://",
863
+ scheme: "imtokenv2://wc",
864
+ store: {
865
+ ios: "https://apps.apple.com/us/app/imtoken2/id1384798940",
866
+ android: "https://play.google.com/store/apps/details?id=im.token.app"
867
+ }
868
+ },
869
+ ronin: {
870
+ native: "roninwallet://",
871
+ scheme: "roninwallet://wc",
872
+ universal: "https://wallet.roninchain.com/wc",
873
+ store: {
874
+ ios: "https://apps.apple.com/us/app/ronin-wallet/id1592675001",
875
+ android: "https://play.google.com/store/apps/details?id=com.skymavis.genesis"
876
+ }
877
+ },
878
+ coin98: {
879
+ native: "coin98://",
880
+ scheme: "coin98://wc",
881
+ universal: "https://coin98.com/wc",
882
+ store: {
883
+ ios: "https://apps.apple.com/vn/app/coin98-wallet/id1561969966",
884
+ android: "https://play.google.com/store/apps/details?id=coin98.crypto.finance.media&hl=vi&gl=US"
885
+ }
886
+ },
887
+ bitkeep: {
888
+ native: "bitkeep://",
889
+ scheme: "bitkeep://wc",
890
+ universal: "https://bkapp.vip/wc",
891
+ store: {
892
+ ios: "https://web3.bitget.com/en/wallet-download?type=0",
893
+ android: "https://web3.bitget.com/en/wallet-download?type=0"
894
+ }
895
+ },
896
+ "1inch": {
897
+ native: "oneinch://open/nobodywilleveruseit",
898
+ scheme: "oneinch://open/nobodywilleveruseit/wc",
899
+ universal: "https://wallet.1inch.io/app/nobodywilleveruseit/wc",
900
+ store: {
901
+ ios: "https://apps.apple.com/us/app/1inch-defi-wallet/id1546049391",
902
+ android: "https://play.google.com/store/apps/details?id=io.oneinch.android"
903
+ }
904
+ },
905
+ ledgerlive: {
906
+ native: "ledgerlive://",
907
+ scheme: "ledgerlive://wc",
908
+ store: {
909
+ ios: "https://itunes.apple.com/app/id1361671700",
910
+ android: "https://play.google.com/store/apps/details?id=com.ledger.live"
911
+ }
912
+ },
913
+ atomic: {
914
+ native: "atomicwallet://",
915
+ scheme: "atomicwallet://wc",
916
+ store: {
917
+ ios: "https://apps.apple.com/us/app/atomic-wallet/id1478257827",
918
+ android: "https://play.google.com/store/apps/details?id=io.atomicwallet"
919
+ }
920
+ },
921
+ alpha: {
922
+ native: "awallet://",
923
+ scheme: "awallet://wc",
924
+ universal: "https://aw.app/wc",
925
+ store: {
926
+ ios: "https://apps.apple.com/us/app/alphawallet-eth-wallet/id1358230430",
927
+ android: "https://play.google.com/store/apps/details?id=io.stormbird.wallet"
928
+ }
929
+ },
930
+ math: {
931
+ native: "mathwallet://",
932
+ scheme: "mathwallet://wc",
933
+ universal: "https://www.mathwallet.org/wc",
934
+ store: {
935
+ ios: "https://apps.apple.com/us/app/mathwallet5/id1582612388",
936
+ android: "https://play.google.com/store/apps/details?id=com.mathwallet.android"
937
+ }
938
+ },
939
+ bitpay: {
940
+ native: "bitpay://",
941
+ scheme: "bitpay://wc",
942
+ universal: "https://link.bitpay.com/wc",
943
+ store: {
944
+ ios: "https://bitpay.onelink.me/Cenw/ejjaw7bs",
945
+ android: "https://bitpay.onelink.me/Cenw/ejjaw7bs"
946
+ }
947
+ },
948
+ rabby: {
949
+ native: "rabby://",
950
+ scheme: "rabby://wc",
951
+ store: {
952
+ ios: "https://apps.apple.com/us/app/rabby-wallet-crypto-evm/id6474381673",
953
+ android: "https://play.google.com/store/apps/details?id=com.debank.rabbymobile"
954
+ }
955
+ },
669
956
  // Bybit's WC deep link nests the uri inside a targetUrl wrapper (bybitapp://open/route?targetUrl=by://web3/walletconnect/wc?uri=...), which the `${scheme}?uri=` builder can't express — QR/scan connect only.
670
- bybit: { "store": { "ios": "https://apps.apple.com/us/app/bybit-buy-trade-crypto/id1488296980", "android": "https://play.google.com/store/apps/details?id=com.bybit.app" } },
671
- binance: { "native": "bnc://app.binance.com/cedefi/", "scheme": "bnc://app.binance.com/cedefi/wc", "store": { "ios": "https://apps.apple.com/us/app/id1436799971", "android": "https://play.google.com/store/apps/details?id=com.binance.dev" } },
672
- gate: { "native": "gtweb3wallet://", "scheme": "gtweb3wallet://wc", "store": { "ios": "https://apps.apple.com/us/app/gate-io-buy-bitcoin-crypto/id1294998195", "android": "https://play.google.com/store/apps/details?id=com.gateio.gateio" } },
957
+ bybit: {
958
+ store: {
959
+ ios: "https://apps.apple.com/us/app/bybit-buy-trade-crypto/id1488296980",
960
+ android: "https://play.google.com/store/apps/details?id=com.bybit.app"
961
+ }
962
+ },
963
+ binance: {
964
+ native: "bnc://app.binance.com/cedefi/",
965
+ scheme: "bnc://app.binance.com/cedefi/wc",
966
+ store: {
967
+ ios: "https://apps.apple.com/us/app/id1436799971",
968
+ android: "https://play.google.com/store/apps/details?id=com.binance.dev"
969
+ }
970
+ },
971
+ gate: {
972
+ native: "gtweb3wallet://",
973
+ scheme: "gtweb3wallet://wc",
974
+ store: {
975
+ ios: "https://apps.apple.com/us/app/gate-io-buy-bitcoin-crypto/id1294998195",
976
+ android: "https://play.google.com/store/apps/details?id=com.gateio.gateio"
977
+ }
978
+ },
673
979
  // Core: `core://` scheme not corroborated to 2 sources — QR/scan connect only.
674
- core: { "store": { "ios": "https://apps.apple.com/us/app/core-wallet/id6443685999", "android": "https://play.google.com/store/apps/details?id=com.avaxwallet" } },
980
+ core: {
981
+ store: {
982
+ ios: "https://apps.apple.com/us/app/core-wallet/id6443685999",
983
+ android: "https://play.google.com/store/apps/details?id=com.avaxwallet"
984
+ }
985
+ },
675
986
  // Backpack: `backpack://` scheme not corroborated to 2 sources — QR/scan connect only.
676
- backpack: { "store": { "ios": "https://apps.apple.com/us/app/backpack-wallet-exchange/id6445964121", "android": "https://play.google.com/store/apps/details?id=app.backpack.mobile" } },
677
- frontier: { "native": "frontier://", "scheme": "frontier://wc", "store": { "ios": "https://apps.apple.com/us/app/frontier-crypto-defi-wallet/id1482380988", "android": "https://play.google.com/store/apps/details?id=com.frontierwallet" } },
678
- onekey: { "native": "onekey-wallet://", "scheme": "onekey-wallet://wc", "store": { "ios": "https://apps.apple.com/us/app/onekey-open-source-wallet/id1609559473", "android": "https://play.google.com/store/apps/details?id=so.onekey.app.wallet" } },
679
- kraken: { "native": "krakenwallet://", "scheme": "krakenwallet://wc", "store": { "ios": "https://apps.apple.com/us/app/kraken-wallet/id1626327149", "android": "https://play.google.com/store/apps/details?id=com.kraken.superwallet" } },
680
- uniswap: { "native": "uniswap://", "scheme": "uniswap://wc", "store": { "ios": "https://apps.apple.com/us/app/uniswap-crypto-nft-wallet/id6443944476", "android": "https://play.google.com/store/apps/details?id=com.uniswap.mobile" } },
681
- omni: { "native": "omni://", "scheme": "omni://wc", "store": { "ios": "https://apps.apple.com/us/app/omni-web3-wallet/id1569375204", "android": "https://play.google.com/store/apps/details?id=fi.steakwallet.app" } },
682
- mew: { "universal": "https://mewwallet.com/wc", "store": { "ios": "https://apps.apple.com/app/id1464614025", "android": "https://play.google.com/store/apps/details?id=com.myetherwallet.mewwallet" } },
683
- foxwallet: { "native": "foxwallet://", "scheme": "foxwallet://wc", "universal": "https://link.foxwallet.com/wc", "store": { "ios": "https://apps.apple.com/app/foxwallet-crypto-web3/id1590983231", "android": "https://play.google.com/store/apps/details?id=com.foxwallet.play" } },
684
- nova: { "native": "novawallet://", "scheme": "novawallet://wc", "store": { "ios": "https://apps.apple.com/us/app/nova-polkadot-wallet/id1597119355", "android": "https://play.google.com/store/apps/details?id=io.novafoundation.nova.market" } },
685
- subwallet: { "native": "subwallet://", "scheme": "subwallet://wc", "store": { "ios": "https://apps.apple.com/us/app/subwallet-polkadot-wallet/id1633050285", "android": "https://play.google.com/store/apps/details?id=app.subwallet.mobile" } },
686
- safe: { "native": "safe://", "scheme": "safe://wc", "store": { "ios": "https://apps.apple.com/us/app/safe-mobile/id6748754793", "android": "https://play.google.com/store/apps/details?id=global.safe.mobileapp" } },
687
- robinhood: { "native": "robinhood-wallet://", "scheme": "robinhood-wallet://wc", "store": { "ios": "https://apps.apple.com/us/app/robinhood-wallet-swap-crypto/id1634080733", "android": "https://play.google.com/store/apps/details?id=com.robinhood.gateway" } },
688
- tangem: { "native": "tangem://", "scheme": "tangem://wc", "store": { "ios": "https://apps.apple.com/app/tangem/id1354868448", "android": "https://play.google.com/store/apps/details?id=com.tangem.wallet" } },
987
+ backpack: {
988
+ store: {
989
+ ios: "https://apps.apple.com/us/app/backpack-wallet-exchange/id6445964121",
990
+ android: "https://play.google.com/store/apps/details?id=app.backpack.mobile"
991
+ }
992
+ },
993
+ frontier: {
994
+ native: "frontier://",
995
+ scheme: "frontier://wc",
996
+ store: {
997
+ ios: "https://apps.apple.com/us/app/frontier-crypto-defi-wallet/id1482380988",
998
+ android: "https://play.google.com/store/apps/details?id=com.frontierwallet"
999
+ }
1000
+ },
1001
+ onekey: {
1002
+ native: "onekey-wallet://",
1003
+ scheme: "onekey-wallet://wc",
1004
+ store: {
1005
+ ios: "https://apps.apple.com/us/app/onekey-open-source-wallet/id1609559473",
1006
+ android: "https://play.google.com/store/apps/details?id=so.onekey.app.wallet"
1007
+ }
1008
+ },
1009
+ kraken: {
1010
+ native: "krakenwallet://",
1011
+ scheme: "krakenwallet://wc",
1012
+ store: {
1013
+ ios: "https://apps.apple.com/us/app/kraken-wallet/id1626327149",
1014
+ android: "https://play.google.com/store/apps/details?id=com.kraken.superwallet"
1015
+ }
1016
+ },
1017
+ uniswap: {
1018
+ native: "uniswap://",
1019
+ scheme: "uniswap://wc",
1020
+ store: {
1021
+ ios: "https://apps.apple.com/us/app/uniswap-crypto-nft-wallet/id6443944476",
1022
+ android: "https://play.google.com/store/apps/details?id=com.uniswap.mobile"
1023
+ }
1024
+ },
1025
+ omni: {
1026
+ native: "omni://",
1027
+ scheme: "omni://wc",
1028
+ store: {
1029
+ ios: "https://apps.apple.com/us/app/omni-web3-wallet/id1569375204",
1030
+ android: "https://play.google.com/store/apps/details?id=fi.steakwallet.app"
1031
+ }
1032
+ },
1033
+ mew: {
1034
+ universal: "https://mewwallet.com/wc",
1035
+ store: {
1036
+ ios: "https://apps.apple.com/app/id1464614025",
1037
+ android: "https://play.google.com/store/apps/details?id=com.myetherwallet.mewwallet"
1038
+ }
1039
+ },
1040
+ foxwallet: {
1041
+ native: "foxwallet://",
1042
+ scheme: "foxwallet://wc",
1043
+ universal: "https://link.foxwallet.com/wc",
1044
+ store: {
1045
+ ios: "https://apps.apple.com/app/foxwallet-crypto-web3/id1590983231",
1046
+ android: "https://play.google.com/store/apps/details?id=com.foxwallet.play"
1047
+ }
1048
+ },
1049
+ nova: {
1050
+ native: "novawallet://",
1051
+ scheme: "novawallet://wc",
1052
+ store: {
1053
+ ios: "https://apps.apple.com/us/app/nova-polkadot-wallet/id1597119355",
1054
+ android: "https://play.google.com/store/apps/details?id=io.novafoundation.nova.market"
1055
+ }
1056
+ },
1057
+ subwallet: {
1058
+ native: "subwallet://",
1059
+ scheme: "subwallet://wc",
1060
+ store: {
1061
+ ios: "https://apps.apple.com/us/app/subwallet-polkadot-wallet/id1633050285",
1062
+ android: "https://play.google.com/store/apps/details?id=app.subwallet.mobile"
1063
+ }
1064
+ },
1065
+ safe: {
1066
+ native: "safe://",
1067
+ scheme: "safe://wc",
1068
+ store: {
1069
+ ios: "https://apps.apple.com/us/app/safe-mobile/id6748754793",
1070
+ android: "https://play.google.com/store/apps/details?id=global.safe.mobileapp"
1071
+ }
1072
+ },
1073
+ robinhood: {
1074
+ native: "robinhood-wallet://",
1075
+ scheme: "robinhood-wallet://wc",
1076
+ store: {
1077
+ ios: "https://apps.apple.com/us/app/robinhood-wallet-swap-crypto/id1634080733",
1078
+ android: "https://play.google.com/store/apps/details?id=com.robinhood.gateway"
1079
+ }
1080
+ },
1081
+ tangem: {
1082
+ native: "tangem://",
1083
+ scheme: "tangem://wc",
1084
+ store: {
1085
+ ios: "https://apps.apple.com/app/tangem/id1354868448",
1086
+ android: "https://play.google.com/store/apps/details?id=com.tangem.wallet"
1087
+ }
1088
+ },
689
1089
  // Trezor Suite: WalletConnect pairing is QR/paste-into-Suite (no corroborated custom scheme) — QR connect only.
690
- trezor: { "store": { "ios": "https://apps.apple.com/us/app/trezor-suite/id1631884497", "android": "https://play.google.com/store/apps/details?id=io.trezor.suite" } },
691
- kucoin: { "native": "kucoin://", "scheme": "kucoin:///wallet/walletConnect", "store": { "ios": "https://apps.apple.com/app/kucoin-buy-bitcoin-crypto/id1378956601", "android": "https://play.google.com/store/apps/details?id=com.kubi.kucoin" } },
1090
+ trezor: {
1091
+ store: {
1092
+ ios: "https://apps.apple.com/us/app/trezor-suite/id1631884497",
1093
+ android: "https://play.google.com/store/apps/details?id=io.trezor.suite"
1094
+ }
1095
+ },
1096
+ kucoin: {
1097
+ native: "kucoin://",
1098
+ scheme: "kucoin:///wallet/walletConnect",
1099
+ store: {
1100
+ ios: "https://apps.apple.com/app/kucoin-buy-bitcoin-crypto/id1378956601",
1101
+ android: "https://play.google.com/store/apps/details?id=com.kubi.kucoin"
1102
+ }
1103
+ },
692
1104
  // Jupiter (Solana): no corroborated WC deep-link scheme — QR connect only.
693
- jupiter: { "store": { "ios": "https://apps.apple.com/us/app/jupiter-solana-swap-wallet/id6484069059", "android": "https://play.google.com/store/apps/details?id=ag.jup.jupiter.android" } },
694
- zengo: { "native": "zengo://get.zengo.com/", "scheme": "zengo://get.zengo.com/wc", "store": { "ios": "https://apps.apple.com/us/app/zengo-crypto-bitcoin-wallet/id1440147115", "android": "https://play.google.com/store/apps/details?id=com.zengo.wallet" } },
1105
+ jupiter: {
1106
+ store: {
1107
+ ios: "https://apps.apple.com/us/app/jupiter-solana-swap-wallet/id6484069059",
1108
+ android: "https://play.google.com/store/apps/details?id=ag.jup.jupiter.android"
1109
+ }
1110
+ },
1111
+ zengo: {
1112
+ native: "zengo://get.zengo.com/",
1113
+ scheme: "zengo://get.zengo.com/wc",
1114
+ store: {
1115
+ ios: "https://apps.apple.com/us/app/zengo-crypto-bitcoin-wallet/id1440147115",
1116
+ android: "https://play.google.com/store/apps/details?id=com.zengo.wallet"
1117
+ }
1118
+ },
695
1119
  // Bitcoin.com Wallet: no corroborated WC deep-link scheme — QR connect only.
696
- bitcoincom: { "store": { "ios": "https://apps.apple.com/us/app/bitcoin-wallet-by-bitcoin-com/id1252903728", "android": "https://play.google.com/store/apps/details?id=com.bitcoin.mwallet" } },
697
- keplr: { "native": "keplrwallet://", "scheme": "keplrwallet://wcV2", "store": { "ios": "https://apps.apple.com/us/app/keplr-wallet/id1567851089", "android": "https://play.google.com/store/apps/details?id=com.chainapsis.keplr" } },
698
- cake: { "native": "cakewallet://", "scheme": "cakewallet://wc", "store": { "ios": "https://apps.apple.com/us/app/cake-wallet/id1334702542", "android": "https://play.google.com/store/apps/details?id=com.cakewallet.cake_wallet" } },
699
- unstoppable: { "native": "unstoppable.money://", "scheme": "unstoppable.money://wc", "store": { "ios": "https://apps.apple.com/us/app/unstoppable-crypto-wallet/id1447619907", "android": "https://play.google.com/store/apps/details?id=io.horizontalsystems.bankwallet" } },
700
- bifrost: { "native": "bifrostwallet://", "scheme": "bifrostwallet://wc", "store": { "ios": "https://apps.apple.com/us/app/bifrost-wallet/id1577198351", "android": "https://play.google.com/store/apps/details?id=com.bifrostwallet.app" } },
701
- pintu: { "native": "pintu://web3wallet", "scheme": "pintu://web3wallet/wc", "store": { "ios": "https://apps.apple.com/id/app/pintu-buy-invest-crypto/id1494119678", "android": "https://play.google.com/store/apps/details?id=com.valar.pintu" } },
702
- hot: { "native": "hotwallet://", "scheme": "hotwallet://wc", "store": { "ios": "https://apps.apple.com/us/app/hot-wallet/id6740916148", "android": "https://play.google.com/store/apps/details?id=app.herewallet.hot" } },
703
- arculus: { "native": "arculuswc://", "scheme": "arculuswc://wc", "universal": "https://gw.arculus.co/app/wc", "store": { "ios": "https://apps.apple.com/us/app/arculus-wallet/id1575425801", "android": "https://play.google.com/store/apps/details?id=co.arculus.wallet.android" } }
1120
+ bitcoincom: {
1121
+ store: {
1122
+ ios: "https://apps.apple.com/us/app/bitcoin-wallet-by-bitcoin-com/id1252903728",
1123
+ android: "https://play.google.com/store/apps/details?id=com.bitcoin.mwallet"
1124
+ }
1125
+ },
1126
+ keplr: {
1127
+ native: "keplrwallet://",
1128
+ scheme: "keplrwallet://wcV2",
1129
+ store: {
1130
+ ios: "https://apps.apple.com/us/app/keplr-wallet/id1567851089",
1131
+ android: "https://play.google.com/store/apps/details?id=com.chainapsis.keplr"
1132
+ }
1133
+ },
1134
+ cake: {
1135
+ native: "cakewallet://",
1136
+ scheme: "cakewallet://wc",
1137
+ store: {
1138
+ ios: "https://apps.apple.com/us/app/cake-wallet/id1334702542",
1139
+ android: "https://play.google.com/store/apps/details?id=com.cakewallet.cake_wallet"
1140
+ }
1141
+ },
1142
+ unstoppable: {
1143
+ native: "unstoppable.money://",
1144
+ scheme: "unstoppable.money://wc",
1145
+ store: {
1146
+ ios: "https://apps.apple.com/us/app/unstoppable-crypto-wallet/id1447619907",
1147
+ android: "https://play.google.com/store/apps/details?id=io.horizontalsystems.bankwallet"
1148
+ }
1149
+ },
1150
+ bifrost: {
1151
+ native: "bifrostwallet://",
1152
+ scheme: "bifrostwallet://wc",
1153
+ store: {
1154
+ ios: "https://apps.apple.com/us/app/bifrost-wallet/id1577198351",
1155
+ android: "https://play.google.com/store/apps/details?id=com.bifrostwallet.app"
1156
+ }
1157
+ },
1158
+ pintu: {
1159
+ native: "pintu://web3wallet",
1160
+ scheme: "pintu://web3wallet/wc",
1161
+ store: {
1162
+ ios: "https://apps.apple.com/id/app/pintu-buy-invest-crypto/id1494119678",
1163
+ android: "https://play.google.com/store/apps/details?id=com.valar.pintu"
1164
+ }
1165
+ },
1166
+ hot: {
1167
+ native: "hotwallet://",
1168
+ scheme: "hotwallet://wc",
1169
+ store: {
1170
+ ios: "https://apps.apple.com/us/app/hot-wallet/id6740916148",
1171
+ android: "https://play.google.com/store/apps/details?id=app.herewallet.hot"
1172
+ }
1173
+ },
1174
+ arculus: {
1175
+ native: "arculuswc://",
1176
+ scheme: "arculuswc://wc",
1177
+ universal: "https://gw.arculus.co/app/wc",
1178
+ store: {
1179
+ ios: "https://apps.apple.com/us/app/arculus-wallet/id1575425801",
1180
+ android: "https://play.google.com/store/apps/details?id=co.arculus.wallet.android"
1181
+ }
1182
+ }
704
1183
  };
705
1184
 
706
1185
  // src/connectors/walletconnect/registry.ts
@@ -717,13 +1196,15 @@ function buildLink(base, uri) {
717
1196
  const sep = base.includes("?") ? "&" : "?";
718
1197
  return `${base}${sep}uri=${encodeURIComponent(uri)}`;
719
1198
  }
720
- function openWallet(link, uri) {
721
- if (typeof document === "undefined") return;
722
- if (link.scheme) {
723
- openHref(buildLink(link.scheme, uri));
724
- } else if (link.universal) {
725
- openHref(buildLink(link.universal, uri));
726
- }
1199
+ function canOpen(link) {
1200
+ return !!(link?.scheme || link?.universal);
1201
+ }
1202
+ function openWallet(link, uri, via = "scheme") {
1203
+ if (typeof document === "undefined") return false;
1204
+ const base = via === "universal" ? link.universal : link.scheme ?? link.universal;
1205
+ if (!base) return false;
1206
+ openHref(buildLink(base, uri));
1207
+ return true;
727
1208
  }
728
1209
  function openWalletApp(link) {
729
1210
  if (typeof document === "undefined") return;
@@ -762,6 +1243,7 @@ function detectAppOpen(timeoutMs) {
762
1243
 
763
1244
  // src/connectors/walletconnect/connector.ts
764
1245
  var STORAGE_KEY = "lydianconnect.wc.topics";
1246
+ var PING_TIMEOUT_MS = 3e3;
765
1247
  var WalletConnectConnector = class extends BaseConnector {
766
1248
  constructor(app, options) {
767
1249
  super();
@@ -772,6 +1254,9 @@ var WalletConnectConnector = class extends BaseConnector {
772
1254
  // claims any wallet not served by an SDK connector
773
1255
  this.client = null;
774
1256
  this.topics = /* @__PURE__ */ new Map();
1257
+ /** walletId -> pairing URI of the connect currently awaiting approval, so a
1258
+ * host-driven open button can fire without the host tracking the URI itself. */
1259
+ this.pending = /* @__PURE__ */ new Map();
775
1260
  this.links = { ...DEFAULT_WALLET_LINKS, ...options.walletLinks ?? {} };
776
1261
  }
777
1262
  isAvailable() {
@@ -815,43 +1300,71 @@ var WalletConnectConnector = class extends BaseConnector {
815
1300
  }
816
1301
  async connect(req) {
817
1302
  const client = await this.getClient();
818
- const reused = this.tryReuse(client, req);
1303
+ const reused = await this.tryReuse(client, req);
819
1304
  if (reused) return reused;
820
1305
  const { uri, approval } = await client.connect({
821
1306
  requiredNamespaces: { [req.namespace.name]: req.namespace.value }
822
1307
  });
823
1308
  if (!uri) throw new Error("WalletConnect did not return a pairing URI");
824
- this.emit({ type: "display_uri", walletId: req.walletId, uri });
825
- if (isMobile()) {
826
- const link = this.links[req.walletId];
827
- if (link) {
828
- openWallet(link, uri);
829
- void detectAppOpen(this.options.openTimeoutMs ?? 2e3).then((opened) => {
830
- if (!opened)
831
- this.emit({
832
- type: "wallet_open_failed",
833
- walletId: req.walletId,
834
- uri,
835
- store: link.store
836
- });
837
- });
838
- }
1309
+ const link = isMobile() ? this.links[req.walletId] : void 0;
1310
+ this.emit({
1311
+ type: "display_uri",
1312
+ walletId: req.walletId,
1313
+ uri,
1314
+ deepLink: canOpen(link)
1315
+ });
1316
+ if (link && canOpen(link)) {
1317
+ this.pending.set(req.walletId, uri);
1318
+ openWallet(link, uri);
1319
+ void detectAppOpen(this.options.openTimeoutMs ?? 2e3).then((opened) => {
1320
+ if (!opened)
1321
+ this.emit({
1322
+ type: "wallet_open_failed",
1323
+ walletId: req.walletId,
1324
+ uri,
1325
+ store: link.store
1326
+ });
1327
+ });
839
1328
  }
840
1329
  const pairingTopic = utils.parseUri(uri).topic;
841
- const session = await withSignal2(approval(), req.signal, () => {
1330
+ const session = await withSignal(approval(), req.signal, () => {
842
1331
  void client.core.pairing.disconnect({ topic: pairingTopic }).catch(() => {
843
1332
  });
844
- });
845
- const connection = this.toConnection(client, req.walletId, session, req.namespace);
1333
+ }).finally(() => this.pending.delete(req.walletId));
1334
+ const connection = this.toConnection(
1335
+ client,
1336
+ req.walletId,
1337
+ session,
1338
+ req.namespace
1339
+ );
846
1340
  this.topics.set(req.walletId, session.topic);
847
1341
  this.persist();
848
1342
  return connection;
849
1343
  }
1344
+ /**
1345
+ * Fire a wallet's deep link on demand. The host calls this from a real click,
1346
+ * which is the one context a mobile browser reliably allows a scheme
1347
+ * navigation from — see the note on {@link openWallet}.
1348
+ */
1349
+ openWallet(input) {
1350
+ const link = this.links[input.walletId];
1351
+ if (!link) return false;
1352
+ const uri = input.uri ?? this.pending.get(input.walletId);
1353
+ if (!uri) {
1354
+ openWalletApp(link);
1355
+ return !!link.native;
1356
+ }
1357
+ return openWallet(link, uri, input.via ?? "scheme");
1358
+ }
850
1359
  async restore() {
851
1360
  const client = await this.getClient();
1361
+ const checked = await Promise.all(
1362
+ [...this.topics].map(
1363
+ async ([walletId, topic]) => [walletId, await this.liveSession(client, topic)]
1364
+ )
1365
+ );
852
1366
  const out = [];
853
- for (const [walletId, topic] of [...this.topics]) {
854
- const session = this.liveSession(client, topic);
1367
+ for (const [walletId, session] of checked) {
855
1368
  if (!session) {
856
1369
  this.topics.delete(walletId);
857
1370
  continue;
@@ -866,19 +1379,26 @@ var WalletConnectConnector = class extends BaseConnector {
866
1379
  const client = this.client;
867
1380
  if (client) {
868
1381
  await Promise.allSettled(
869
- client.session.getAll().map((s) => client.disconnect({ topic: s.topic, reason: utils.getSdkError("USER_DISCONNECTED") }))
1382
+ client.session.getAll().map(
1383
+ (s) => client.disconnect({
1384
+ topic: s.topic,
1385
+ reason: utils.getSdkError("USER_DISCONNECTED")
1386
+ })
1387
+ )
870
1388
  );
871
1389
  }
872
1390
  this.topics.clear();
1391
+ this.pending.clear();
873
1392
  this.persist();
874
1393
  }
875
1394
  // --- helpers ---
876
- tryReuse(client, req) {
1395
+ async tryReuse(client, req) {
877
1396
  const topic = this.topics.get(req.walletId);
878
1397
  if (!topic) return null;
879
- const session = this.liveSession(client, topic);
1398
+ const session = await this.liveSession(client, topic);
880
1399
  if (!session) {
881
1400
  this.topics.delete(req.walletId);
1401
+ this.persist();
882
1402
  return null;
883
1403
  }
884
1404
  const ns = session.namespaces[req.namespace.name];
@@ -889,7 +1409,9 @@ var WalletConnectConnector = class extends BaseConnector {
889
1409
  toConnection(client, walletId, session, namespace) {
890
1410
  const ns = session.namespaces[namespace.name];
891
1411
  const account = (ns?.accounts ?? []).map((a) => a.split(":")[2]).find((a) => !!a) ?? "";
892
- const chainId = ns?.chains?.[0] ?? (ns?.accounts?.[0] ? ns.accounts[0].split(":").slice(0, 2).join(":") : `${namespace.name}:1`);
1412
+ const approved = ns?.chains?.length ? ns.chains : (ns?.accounts ?? []).map((a) => a.split(":").slice(0, 2).join(":"));
1413
+ const wanted = namespace.value.chains?.[0];
1414
+ const chainId = wanted && approved.includes(wanted) ? wanted : approved[0] ?? `${namespace.name}:1`;
893
1415
  const topic = session.topic;
894
1416
  return {
895
1417
  walletId,
@@ -914,12 +1436,27 @@ var WalletConnectConnector = class extends BaseConnector {
914
1436
  }
915
1437
  };
916
1438
  }
917
- liveSession(client, topic) {
1439
+ /**
1440
+ * A stored session the wallet is still on the other end of. The local record
1441
+ * is not evidence — it survives the user clearing the dApp inside their
1442
+ * wallet — and reusing a dead one returns a Connection that looks healthy
1443
+ * while every request hangs. So the peer has to answer for it.
1444
+ */
1445
+ async liveSession(client, topic) {
1446
+ let session;
918
1447
  try {
919
- return client.session.get(topic);
1448
+ session = client.session.get(topic);
920
1449
  } catch {
921
1450
  return void 0;
922
1451
  }
1452
+ let timer;
1453
+ const alive = await Promise.race([
1454
+ client.ping({ topic }).then(() => true).catch(() => false),
1455
+ new Promise((resolve) => {
1456
+ timer = setTimeout(() => resolve(false), PING_TIMEOUT_MS);
1457
+ })
1458
+ ]).finally(() => clearTimeout(timer));
1459
+ return alive ? session : void 0;
923
1460
  }
924
1461
  walletForTopic(topic) {
925
1462
  for (const [walletId, t] of this.topics) if (t === topic) return walletId;
@@ -934,7 +1471,8 @@ var WalletConnectConnector = class extends BaseConnector {
934
1471
  load() {
935
1472
  try {
936
1473
  const raw = localStorage.getItem(STORAGE_KEY);
937
- if (raw) this.topics = new Map(JSON.parse(raw));
1474
+ if (raw)
1475
+ this.topics = new Map(JSON.parse(raw));
938
1476
  } catch {
939
1477
  }
940
1478
  }
@@ -944,38 +1482,18 @@ function firstNamespace(session) {
944
1482
  if (!name) return null;
945
1483
  const v = session.namespaces[name];
946
1484
  if (!v) return null;
947
- return { name, value: { chains: v.chains, methods: v.methods, events: v.events } };
948
- }
949
- function withSignal2(promise, signal, onAbort) {
950
- if (!signal) return promise;
951
- if (signal.aborted) {
952
- onAbort();
953
- return Promise.reject(new DOMException("Aborted", "AbortError"));
954
- }
955
- return new Promise((resolve, reject) => {
956
- const abortHandler = () => {
957
- onAbort();
958
- reject(new DOMException("Aborted", "AbortError"));
959
- };
960
- signal.addEventListener("abort", abortHandler, { once: true });
961
- promise.then(
962
- (v) => {
963
- signal.removeEventListener("abort", abortHandler);
964
- resolve(v);
965
- },
966
- (e) => {
967
- signal.removeEventListener("abort", abortHandler);
968
- reject(e);
969
- }
970
- );
971
- });
1485
+ return {
1486
+ name,
1487
+ value: { chains: v.chains, methods: v.methods, events: v.events }
1488
+ };
972
1489
  }
973
1490
 
974
1491
  // src/core/lydian-connect.ts
975
1492
  function injectedRdnsMap(sdkServed) {
976
1493
  const map = {};
977
1494
  for (const w of WALLET_CATALOG) {
978
- if (w.rdns && w.namespaces.includes("eip155") && !sdkServed.has(w.id)) map[w.id] = w.rdns;
1495
+ if (w.rdns && w.namespaces.includes("eip155") && !sdkServed.has(w.id))
1496
+ map[w.id] = w.rdns;
979
1497
  }
980
1498
  return map;
981
1499
  }
@@ -990,7 +1508,9 @@ function createConnectors(config) {
990
1508
  return [
991
1509
  ...sdk,
992
1510
  new InjectedConnector(injectedRdnsMap(sdkServed)),
993
- new WalletConnectConnector(app, { projectId: config.walletConnectProjectId })
1511
+ new WalletConnectConnector(app, {
1512
+ projectId: config.walletConnectProjectId
1513
+ })
994
1514
  ];
995
1515
  }
996
1516
  var LydianConnect = class {
@@ -1002,7 +1522,9 @@ var LydianConnect = class {
1002
1522
  }
1003
1523
  async connect(input) {
1004
1524
  if (input.chainId === void 0 || input.chainId === null) {
1005
- throw new Error('connect() requires a chainId (a number like 137 or an "eip155:<id>" string)');
1525
+ throw new Error(
1526
+ 'connect() requires a chainId (a number like 137 or an "eip155:<id>" string)'
1527
+ );
1006
1528
  }
1007
1529
  return this.manager.connect({
1008
1530
  walletId: input.walletId,
@@ -1010,6 +1532,20 @@ var LydianConnect = class {
1010
1532
  signal: input.signal
1011
1533
  });
1012
1534
  }
1535
+ /**
1536
+ * Open a wallet app, optionally handing it the pairing URI from `display_uri`.
1537
+ *
1538
+ * Call this from a real click handler. A mobile browser will suppress the same
1539
+ * navigation issued automatically after an async step, which is why the
1540
+ * `display_uri` event reports `deepLink` — that is the library asking the host
1541
+ * for a button to put this behind.
1542
+ *
1543
+ * Returns whether a link was fired. `false` means nothing is known for this
1544
+ * wallet on this device; show the QR code instead.
1545
+ */
1546
+ openWallet(input) {
1547
+ return this.manager.openWallet(input);
1548
+ }
1013
1549
  restore() {
1014
1550
  return this.manager.restore();
1015
1551
  }