@lydianpay/lydianconnect 1.2.1 → 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 +243 -159
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -2
- package/dist/index.d.ts +40 -2
- package/dist/index.js +243 -159
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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(() => {
|
|
@@ -238,10 +246,15 @@ var WALLET_CATALOG = WALLETS.map((w) => ({
|
|
|
238
246
|
}));
|
|
239
247
|
|
|
240
248
|
// src/core/connector.ts
|
|
249
|
+
function requestedChain(req) {
|
|
250
|
+
return req.namespace.value.chains?.[0];
|
|
251
|
+
}
|
|
241
252
|
var BaseConnector = class {
|
|
242
253
|
constructor() {
|
|
243
254
|
this.walletIds = [];
|
|
244
255
|
this.handlers = /* @__PURE__ */ new Set();
|
|
256
|
+
/** Depth of in-flight connects; see {@link duringConnect}. */
|
|
257
|
+
this.connecting = 0;
|
|
245
258
|
}
|
|
246
259
|
servesNamespace(_namespaceName) {
|
|
247
260
|
return true;
|
|
@@ -255,7 +268,28 @@ var BaseConnector = class {
|
|
|
255
268
|
this.handlers.add(handler);
|
|
256
269
|
return () => this.handlers.delete(handler);
|
|
257
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
|
+
}
|
|
258
289
|
emit(event) {
|
|
290
|
+
if (this.connecting > 0 && (event.type === "chainChanged" || event.type === "accountsChanged")) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
259
293
|
for (const handler of this.handlers) handler(event);
|
|
260
294
|
}
|
|
261
295
|
};
|
|
@@ -265,6 +299,80 @@ function deriveUrl() {
|
|
|
265
299
|
return typeof window !== "undefined" ? window.location.origin : "";
|
|
266
300
|
}
|
|
267
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
|
+
|
|
268
376
|
// src/connectors/metamask/connector.ts
|
|
269
377
|
var WALLET_ID = "metamask";
|
|
270
378
|
var MetaMaskConnector = class extends BaseConnector {
|
|
@@ -322,17 +430,23 @@ var MetaMaskConnector = class extends BaseConnector {
|
|
|
322
430
|
);
|
|
323
431
|
}
|
|
324
432
|
async connect(req) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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);
|
|
328
449
|
});
|
|
329
|
-
const account = accounts?.[0];
|
|
330
|
-
if (!account) throw new Error("MetaMask returned no account");
|
|
331
|
-
await this.ensureChain(provider, req);
|
|
332
|
-
const chainId = toCaip(
|
|
333
|
-
await provider.request({ method: "eth_chainId" })
|
|
334
|
-
);
|
|
335
|
-
return this.toConnection(provider, account, chainId);
|
|
336
450
|
}
|
|
337
451
|
async restore() {
|
|
338
452
|
const provider = await this.getProvider().catch(() => null);
|
|
@@ -350,22 +464,6 @@ var MetaMaskConnector = class extends BaseConnector {
|
|
|
350
464
|
this.provider = null;
|
|
351
465
|
this.listenersBound = false;
|
|
352
466
|
}
|
|
353
|
-
/** Best-effort switch to the requested chain; leaves the wallet as-is if it's unknown to it. */
|
|
354
|
-
async ensureChain(provider, req) {
|
|
355
|
-
const wanted = req.namespace.value.chains?.[0];
|
|
356
|
-
const wantedNum = wanted ? Number(wanted.split(":")[1]) : NaN;
|
|
357
|
-
if (!Number.isFinite(wantedNum) || wantedNum <= 0) return;
|
|
358
|
-
const current = parseInt(
|
|
359
|
-
await provider.request({ method: "eth_chainId" }).catch(() => "0x0") ?? "0x0",
|
|
360
|
-
16
|
|
361
|
-
);
|
|
362
|
-
if (current === wantedNum) return;
|
|
363
|
-
await provider.request({
|
|
364
|
-
method: "wallet_switchEthereumChain",
|
|
365
|
-
params: [{ chainId: `0x${wantedNum.toString(16)}` }]
|
|
366
|
-
}).catch(() => {
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
467
|
toConnection(provider, account, chainId) {
|
|
370
468
|
return {
|
|
371
469
|
walletId: WALLET_ID,
|
|
@@ -387,20 +485,18 @@ var MetaMaskConnector = class extends BaseConnector {
|
|
|
387
485
|
};
|
|
388
486
|
}
|
|
389
487
|
};
|
|
390
|
-
function
|
|
391
|
-
|
|
392
|
-
const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
|
|
393
|
-
return `eip155:${Number.isFinite(num) ? num : 1}`;
|
|
488
|
+
function rpcFor(provider) {
|
|
489
|
+
return (args) => provider.request(args);
|
|
394
490
|
}
|
|
395
491
|
|
|
396
492
|
// src/connectors/coinbase/connector.ts
|
|
493
|
+
var WALLET_ID2 = "coinbase";
|
|
397
494
|
function rpc(provider, method, params) {
|
|
398
495
|
return provider.request({
|
|
399
496
|
method,
|
|
400
497
|
params
|
|
401
498
|
});
|
|
402
499
|
}
|
|
403
|
-
var WALLET_ID2 = "coinbase";
|
|
404
500
|
var CoinbaseConnector = class extends BaseConnector {
|
|
405
501
|
constructor(app) {
|
|
406
502
|
super();
|
|
@@ -437,7 +533,7 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
437
533
|
this.emit({
|
|
438
534
|
type: "chainChanged",
|
|
439
535
|
walletId: WALLET_ID2,
|
|
440
|
-
chainId:
|
|
536
|
+
chainId: toCaip(String(hex))
|
|
441
537
|
});
|
|
442
538
|
});
|
|
443
539
|
provider.on("accountsChanged", (accounts) => {
|
|
@@ -452,13 +548,18 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
452
548
|
);
|
|
453
549
|
}
|
|
454
550
|
async connect(req) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
+
});
|
|
462
563
|
}
|
|
463
564
|
async restore() {
|
|
464
565
|
const provider = await this.getProvider().catch(() => null);
|
|
@@ -468,7 +569,7 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
468
569
|
);
|
|
469
570
|
const account = accounts?.[0];
|
|
470
571
|
if (!account) return [];
|
|
471
|
-
const chainId =
|
|
572
|
+
const chainId = toCaip(
|
|
472
573
|
await rpc(provider, "eth_chainId").catch(() => "0x1")
|
|
473
574
|
);
|
|
474
575
|
return [this.toConnection(provider, account, chainId)];
|
|
@@ -479,20 +580,6 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
479
580
|
this.provider = null;
|
|
480
581
|
this.listenersBound = false;
|
|
481
582
|
}
|
|
482
|
-
async ensureChain(provider, req) {
|
|
483
|
-
const wanted = req.namespace.value.chains?.[0];
|
|
484
|
-
const wantedNum = wanted ? Number(wanted.split(":")[1]) : NaN;
|
|
485
|
-
if (!Number.isFinite(wantedNum) || wantedNum <= 0) return;
|
|
486
|
-
const current = parseInt(
|
|
487
|
-
await rpc(provider, "eth_chainId").catch(() => "0x0") ?? "0x0",
|
|
488
|
-
16
|
|
489
|
-
);
|
|
490
|
-
if (current === wantedNum) return;
|
|
491
|
-
await rpc(provider, "wallet_switchEthereumChain", [
|
|
492
|
-
{ chainId: `0x${wantedNum.toString(16)}` }
|
|
493
|
-
]).catch(() => {
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
583
|
toConnection(provider, account, chainId) {
|
|
497
584
|
return {
|
|
498
585
|
walletId: WALLET_ID2,
|
|
@@ -512,10 +599,8 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
512
599
|
};
|
|
513
600
|
}
|
|
514
601
|
};
|
|
515
|
-
function
|
|
516
|
-
|
|
517
|
-
const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
|
|
518
|
-
return `eip155:${Number.isFinite(num) ? num : 1}`;
|
|
602
|
+
function rpcFor2(provider) {
|
|
603
|
+
return ({ method, params }) => rpc(provider, method, params);
|
|
519
604
|
}
|
|
520
605
|
function evmChainIds(req) {
|
|
521
606
|
const wanted = req.namespace.value.chains?.[0];
|
|
@@ -579,8 +664,8 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
579
664
|
const account = accounts?.[0];
|
|
580
665
|
if (!account)
|
|
581
666
|
throw new Error(`${req.walletId} extension returned no account`);
|
|
582
|
-
await
|
|
583
|
-
const chainId =
|
|
667
|
+
await ensureChain({ rpc: rpcFor3(provider), chainId: requestedChain(req) });
|
|
668
|
+
const chainId = toCaip(
|
|
584
669
|
await provider.request({ method: "eth_chainId" })
|
|
585
670
|
);
|
|
586
671
|
this.bindEvents(provider, req.walletId);
|
|
@@ -599,7 +684,7 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
599
684
|
const accounts = await provider.request({ method: "eth_accounts" }).catch(() => []);
|
|
600
685
|
const account = accounts?.[0];
|
|
601
686
|
if (!account) continue;
|
|
602
|
-
const chainId =
|
|
687
|
+
const chainId = toCaip(
|
|
603
688
|
await provider.request({ method: "eth_chainId" }).catch(() => "0x1")
|
|
604
689
|
);
|
|
605
690
|
this.bindEvents(provider, walletId);
|
|
@@ -618,7 +703,7 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
618
703
|
this.emit({
|
|
619
704
|
type: "chainChanged",
|
|
620
705
|
walletId,
|
|
621
|
-
chainId:
|
|
706
|
+
chainId: toCaip(String(hex))
|
|
622
707
|
});
|
|
623
708
|
});
|
|
624
709
|
provider.on("accountsChanged", (accounts) => {
|
|
@@ -631,22 +716,6 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
631
716
|
() => this.emit({ type: "disconnect", walletId })
|
|
632
717
|
);
|
|
633
718
|
}
|
|
634
|
-
/** Best-effort switch to the requested chain; leaves the wallet as-is if it's unknown to it. */
|
|
635
|
-
async ensureChain(provider, req) {
|
|
636
|
-
const wanted = req.namespace.value.chains?.[0];
|
|
637
|
-
const wantedNum = wanted ? Number(wanted.split(":")[1]) : NaN;
|
|
638
|
-
if (!Number.isFinite(wantedNum) || wantedNum <= 0) return;
|
|
639
|
-
const current = parseInt(
|
|
640
|
-
await provider.request({ method: "eth_chainId" }).catch(() => "0x0") ?? "0x0",
|
|
641
|
-
16
|
|
642
|
-
);
|
|
643
|
-
if (current === wantedNum) return;
|
|
644
|
-
await provider.request({
|
|
645
|
-
method: "wallet_switchEthereumChain",
|
|
646
|
-
params: [{ chainId: `0x${wantedNum.toString(16)}` }]
|
|
647
|
-
}).catch(() => {
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
719
|
toConnection(provider, walletId, account, chainId) {
|
|
651
720
|
return {
|
|
652
721
|
walletId,
|
|
@@ -670,29 +739,8 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
670
739
|
};
|
|
671
740
|
}
|
|
672
741
|
};
|
|
673
|
-
function
|
|
674
|
-
|
|
675
|
-
const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
|
|
676
|
-
return `eip155:${Number.isFinite(num) ? num : 1}`;
|
|
677
|
-
}
|
|
678
|
-
function withSignal(promise, signal) {
|
|
679
|
-
if (!signal) return promise;
|
|
680
|
-
if (signal.aborted)
|
|
681
|
-
return Promise.reject(new DOMException("Aborted", "AbortError"));
|
|
682
|
-
return new Promise((resolve, reject) => {
|
|
683
|
-
const onAbort = () => reject(new DOMException("Aborted", "AbortError"));
|
|
684
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
685
|
-
promise.then(
|
|
686
|
-
(value) => {
|
|
687
|
-
signal.removeEventListener("abort", onAbort);
|
|
688
|
-
resolve(value);
|
|
689
|
-
},
|
|
690
|
-
(error) => {
|
|
691
|
-
signal.removeEventListener("abort", onAbort);
|
|
692
|
-
reject(error);
|
|
693
|
-
}
|
|
694
|
-
);
|
|
695
|
-
});
|
|
742
|
+
function rpcFor3(provider) {
|
|
743
|
+
return (args) => provider.request(args);
|
|
696
744
|
}
|
|
697
745
|
|
|
698
746
|
// src/connectors/walletconnect/wallet-links.ts
|
|
@@ -1142,13 +1190,15 @@ function buildLink(base, uri) {
|
|
|
1142
1190
|
const sep = base.includes("?") ? "&" : "?";
|
|
1143
1191
|
return `${base}${sep}uri=${encodeURIComponent(uri)}`;
|
|
1144
1192
|
}
|
|
1145
|
-
function
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
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;
|
|
1152
1202
|
}
|
|
1153
1203
|
function openWalletApp(link) {
|
|
1154
1204
|
if (typeof document === "undefined") return;
|
|
@@ -1187,6 +1237,7 @@ function detectAppOpen(timeoutMs) {
|
|
|
1187
1237
|
|
|
1188
1238
|
// src/connectors/walletconnect/connector.ts
|
|
1189
1239
|
var STORAGE_KEY = "lydianconnect.wc.topics";
|
|
1240
|
+
var PING_TIMEOUT_MS = 3e3;
|
|
1190
1241
|
var WalletConnectConnector = class extends BaseConnector {
|
|
1191
1242
|
constructor(app, options) {
|
|
1192
1243
|
super();
|
|
@@ -1197,6 +1248,9 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1197
1248
|
// claims any wallet not served by an SDK connector
|
|
1198
1249
|
this.client = null;
|
|
1199
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();
|
|
1200
1254
|
this.links = { ...DEFAULT_WALLET_LINKS, ...options.walletLinks ?? {} };
|
|
1201
1255
|
}
|
|
1202
1256
|
isAvailable() {
|
|
@@ -1240,35 +1294,37 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1240
1294
|
}
|
|
1241
1295
|
async connect(req) {
|
|
1242
1296
|
const client = await this.getClient();
|
|
1243
|
-
const reused = this.tryReuse(client, req);
|
|
1297
|
+
const reused = await this.tryReuse(client, req);
|
|
1244
1298
|
if (reused) return reused;
|
|
1245
1299
|
const { uri, approval } = await client.connect({
|
|
1246
1300
|
requiredNamespaces: { [req.namespace.name]: req.namespace.value }
|
|
1247
1301
|
});
|
|
1248
1302
|
if (!uri) throw new Error("WalletConnect did not return a pairing URI");
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
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
|
+
});
|
|
1266
1322
|
}
|
|
1267
1323
|
const pairingTopic = parseUri(uri).topic;
|
|
1268
|
-
const session = await
|
|
1324
|
+
const session = await withSignal(approval(), req.signal, () => {
|
|
1269
1325
|
void client.core.pairing.disconnect({ topic: pairingTopic }).catch(() => {
|
|
1270
1326
|
});
|
|
1271
|
-
});
|
|
1327
|
+
}).finally(() => this.pending.delete(req.walletId));
|
|
1272
1328
|
const connection = this.toConnection(
|
|
1273
1329
|
client,
|
|
1274
1330
|
req.walletId,
|
|
@@ -1279,11 +1335,30 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1279
1335
|
this.persist();
|
|
1280
1336
|
return connection;
|
|
1281
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
|
+
}
|
|
1282
1353
|
async restore() {
|
|
1283
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
|
+
);
|
|
1284
1360
|
const out = [];
|
|
1285
|
-
for (const [walletId,
|
|
1286
|
-
const session = this.liveSession(client, topic);
|
|
1361
|
+
for (const [walletId, session] of checked) {
|
|
1287
1362
|
if (!session) {
|
|
1288
1363
|
this.topics.delete(walletId);
|
|
1289
1364
|
continue;
|
|
@@ -1307,15 +1382,17 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1307
1382
|
);
|
|
1308
1383
|
}
|
|
1309
1384
|
this.topics.clear();
|
|
1385
|
+
this.pending.clear();
|
|
1310
1386
|
this.persist();
|
|
1311
1387
|
}
|
|
1312
1388
|
// --- helpers ---
|
|
1313
|
-
tryReuse(client, req) {
|
|
1389
|
+
async tryReuse(client, req) {
|
|
1314
1390
|
const topic = this.topics.get(req.walletId);
|
|
1315
1391
|
if (!topic) return null;
|
|
1316
|
-
const session = this.liveSession(client, topic);
|
|
1392
|
+
const session = await this.liveSession(client, topic);
|
|
1317
1393
|
if (!session) {
|
|
1318
1394
|
this.topics.delete(req.walletId);
|
|
1395
|
+
this.persist();
|
|
1319
1396
|
return null;
|
|
1320
1397
|
}
|
|
1321
1398
|
const ns = session.namespaces[req.namespace.name];
|
|
@@ -1326,7 +1403,9 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1326
1403
|
toConnection(client, walletId, session, namespace) {
|
|
1327
1404
|
const ns = session.namespaces[namespace.name];
|
|
1328
1405
|
const account = (ns?.accounts ?? []).map((a) => a.split(":")[2]).find((a) => !!a) ?? "";
|
|
1329
|
-
const
|
|
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`;
|
|
1330
1409
|
const topic = session.topic;
|
|
1331
1410
|
return {
|
|
1332
1411
|
walletId,
|
|
@@ -1351,12 +1430,27 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1351
1430
|
}
|
|
1352
1431
|
};
|
|
1353
1432
|
}
|
|
1354
|
-
|
|
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;
|
|
1355
1441
|
try {
|
|
1356
|
-
|
|
1442
|
+
session = client.session.get(topic);
|
|
1357
1443
|
} catch {
|
|
1358
1444
|
return void 0;
|
|
1359
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;
|
|
1360
1454
|
}
|
|
1361
1455
|
walletForTopic(topic) {
|
|
1362
1456
|
for (const [walletId, t] of this.topics) if (t === topic) return walletId;
|
|
@@ -1387,30 +1481,6 @@ function firstNamespace(session) {
|
|
|
1387
1481
|
value: { chains: v.chains, methods: v.methods, events: v.events }
|
|
1388
1482
|
};
|
|
1389
1483
|
}
|
|
1390
|
-
function withSignal2(promise, signal, onAbort) {
|
|
1391
|
-
if (!signal) return promise;
|
|
1392
|
-
if (signal.aborted) {
|
|
1393
|
-
onAbort();
|
|
1394
|
-
return Promise.reject(new DOMException("Aborted", "AbortError"));
|
|
1395
|
-
}
|
|
1396
|
-
return new Promise((resolve, reject) => {
|
|
1397
|
-
const abortHandler = () => {
|
|
1398
|
-
onAbort();
|
|
1399
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
1400
|
-
};
|
|
1401
|
-
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1402
|
-
promise.then(
|
|
1403
|
-
(v) => {
|
|
1404
|
-
signal.removeEventListener("abort", abortHandler);
|
|
1405
|
-
resolve(v);
|
|
1406
|
-
},
|
|
1407
|
-
(e) => {
|
|
1408
|
-
signal.removeEventListener("abort", abortHandler);
|
|
1409
|
-
reject(e);
|
|
1410
|
-
}
|
|
1411
|
-
);
|
|
1412
|
-
});
|
|
1413
|
-
}
|
|
1414
1484
|
|
|
1415
1485
|
// src/core/lydian-connect.ts
|
|
1416
1486
|
function injectedRdnsMap(sdkServed) {
|
|
@@ -1456,6 +1526,20 @@ var LydianConnect = class {
|
|
|
1456
1526
|
signal: input.signal
|
|
1457
1527
|
});
|
|
1458
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
|
+
}
|
|
1459
1543
|
restore() {
|
|
1460
1544
|
return this.manager.restore();
|
|
1461
1545
|
}
|