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