@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.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(() => {
|
|
@@ -244,10 +252,15 @@ var WALLET_CATALOG = WALLETS.map((w) => ({
|
|
|
244
252
|
}));
|
|
245
253
|
|
|
246
254
|
// src/core/connector.ts
|
|
255
|
+
function requestedChain(req) {
|
|
256
|
+
return req.namespace.value.chains?.[0];
|
|
257
|
+
}
|
|
247
258
|
var BaseConnector = class {
|
|
248
259
|
constructor() {
|
|
249
260
|
this.walletIds = [];
|
|
250
261
|
this.handlers = /* @__PURE__ */ new Set();
|
|
262
|
+
/** Depth of in-flight connects; see {@link duringConnect}. */
|
|
263
|
+
this.connecting = 0;
|
|
251
264
|
}
|
|
252
265
|
servesNamespace(_namespaceName) {
|
|
253
266
|
return true;
|
|
@@ -261,7 +274,28 @@ var BaseConnector = class {
|
|
|
261
274
|
this.handlers.add(handler);
|
|
262
275
|
return () => this.handlers.delete(handler);
|
|
263
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
|
+
}
|
|
264
295
|
emit(event) {
|
|
296
|
+
if (this.connecting > 0 && (event.type === "chainChanged" || event.type === "accountsChanged")) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
265
299
|
for (const handler of this.handlers) handler(event);
|
|
266
300
|
}
|
|
267
301
|
};
|
|
@@ -271,6 +305,80 @@ function deriveUrl() {
|
|
|
271
305
|
return typeof window !== "undefined" ? window.location.origin : "";
|
|
272
306
|
}
|
|
273
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
|
+
|
|
274
382
|
// src/connectors/metamask/connector.ts
|
|
275
383
|
var WALLET_ID = "metamask";
|
|
276
384
|
var MetaMaskConnector = class extends BaseConnector {
|
|
@@ -328,17 +436,23 @@ var MetaMaskConnector = class extends BaseConnector {
|
|
|
328
436
|
);
|
|
329
437
|
}
|
|
330
438
|
async connect(req) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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);
|
|
334
455
|
});
|
|
335
|
-
const account = accounts?.[0];
|
|
336
|
-
if (!account) throw new Error("MetaMask returned no account");
|
|
337
|
-
await this.ensureChain(provider, req);
|
|
338
|
-
const chainId = toCaip(
|
|
339
|
-
await provider.request({ method: "eth_chainId" })
|
|
340
|
-
);
|
|
341
|
-
return this.toConnection(provider, account, chainId);
|
|
342
456
|
}
|
|
343
457
|
async restore() {
|
|
344
458
|
const provider = await this.getProvider().catch(() => null);
|
|
@@ -356,22 +470,6 @@ var MetaMaskConnector = class extends BaseConnector {
|
|
|
356
470
|
this.provider = null;
|
|
357
471
|
this.listenersBound = false;
|
|
358
472
|
}
|
|
359
|
-
/** Best-effort switch to the requested chain; leaves the wallet as-is if it's unknown to it. */
|
|
360
|
-
async ensureChain(provider, req) {
|
|
361
|
-
const wanted = req.namespace.value.chains?.[0];
|
|
362
|
-
const wantedNum = wanted ? Number(wanted.split(":")[1]) : NaN;
|
|
363
|
-
if (!Number.isFinite(wantedNum) || wantedNum <= 0) return;
|
|
364
|
-
const current = parseInt(
|
|
365
|
-
await provider.request({ method: "eth_chainId" }).catch(() => "0x0") ?? "0x0",
|
|
366
|
-
16
|
|
367
|
-
);
|
|
368
|
-
if (current === wantedNum) return;
|
|
369
|
-
await provider.request({
|
|
370
|
-
method: "wallet_switchEthereumChain",
|
|
371
|
-
params: [{ chainId: `0x${wantedNum.toString(16)}` }]
|
|
372
|
-
}).catch(() => {
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
473
|
toConnection(provider, account, chainId) {
|
|
376
474
|
return {
|
|
377
475
|
walletId: WALLET_ID,
|
|
@@ -393,20 +491,18 @@ var MetaMaskConnector = class extends BaseConnector {
|
|
|
393
491
|
};
|
|
394
492
|
}
|
|
395
493
|
};
|
|
396
|
-
function
|
|
397
|
-
|
|
398
|
-
const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
|
|
399
|
-
return `eip155:${Number.isFinite(num) ? num : 1}`;
|
|
494
|
+
function rpcFor(provider) {
|
|
495
|
+
return (args) => provider.request(args);
|
|
400
496
|
}
|
|
401
497
|
|
|
402
498
|
// src/connectors/coinbase/connector.ts
|
|
499
|
+
var WALLET_ID2 = "coinbase";
|
|
403
500
|
function rpc(provider, method, params) {
|
|
404
501
|
return provider.request({
|
|
405
502
|
method,
|
|
406
503
|
params
|
|
407
504
|
});
|
|
408
505
|
}
|
|
409
|
-
var WALLET_ID2 = "coinbase";
|
|
410
506
|
var CoinbaseConnector = class extends BaseConnector {
|
|
411
507
|
constructor(app) {
|
|
412
508
|
super();
|
|
@@ -443,7 +539,7 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
443
539
|
this.emit({
|
|
444
540
|
type: "chainChanged",
|
|
445
541
|
walletId: WALLET_ID2,
|
|
446
|
-
chainId:
|
|
542
|
+
chainId: toCaip(String(hex))
|
|
447
543
|
});
|
|
448
544
|
});
|
|
449
545
|
provider.on("accountsChanged", (accounts) => {
|
|
@@ -458,13 +554,18 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
458
554
|
);
|
|
459
555
|
}
|
|
460
556
|
async connect(req) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
+
});
|
|
468
569
|
}
|
|
469
570
|
async restore() {
|
|
470
571
|
const provider = await this.getProvider().catch(() => null);
|
|
@@ -474,7 +575,7 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
474
575
|
);
|
|
475
576
|
const account = accounts?.[0];
|
|
476
577
|
if (!account) return [];
|
|
477
|
-
const chainId =
|
|
578
|
+
const chainId = toCaip(
|
|
478
579
|
await rpc(provider, "eth_chainId").catch(() => "0x1")
|
|
479
580
|
);
|
|
480
581
|
return [this.toConnection(provider, account, chainId)];
|
|
@@ -485,20 +586,6 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
485
586
|
this.provider = null;
|
|
486
587
|
this.listenersBound = false;
|
|
487
588
|
}
|
|
488
|
-
async ensureChain(provider, req) {
|
|
489
|
-
const wanted = req.namespace.value.chains?.[0];
|
|
490
|
-
const wantedNum = wanted ? Number(wanted.split(":")[1]) : NaN;
|
|
491
|
-
if (!Number.isFinite(wantedNum) || wantedNum <= 0) return;
|
|
492
|
-
const current = parseInt(
|
|
493
|
-
await rpc(provider, "eth_chainId").catch(() => "0x0") ?? "0x0",
|
|
494
|
-
16
|
|
495
|
-
);
|
|
496
|
-
if (current === wantedNum) return;
|
|
497
|
-
await rpc(provider, "wallet_switchEthereumChain", [
|
|
498
|
-
{ chainId: `0x${wantedNum.toString(16)}` }
|
|
499
|
-
]).catch(() => {
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
589
|
toConnection(provider, account, chainId) {
|
|
503
590
|
return {
|
|
504
591
|
walletId: WALLET_ID2,
|
|
@@ -518,10 +605,8 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
518
605
|
};
|
|
519
606
|
}
|
|
520
607
|
};
|
|
521
|
-
function
|
|
522
|
-
|
|
523
|
-
const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
|
|
524
|
-
return `eip155:${Number.isFinite(num) ? num : 1}`;
|
|
608
|
+
function rpcFor2(provider) {
|
|
609
|
+
return ({ method, params }) => rpc(provider, method, params);
|
|
525
610
|
}
|
|
526
611
|
function evmChainIds(req) {
|
|
527
612
|
const wanted = req.namespace.value.chains?.[0];
|
|
@@ -585,8 +670,8 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
585
670
|
const account = accounts?.[0];
|
|
586
671
|
if (!account)
|
|
587
672
|
throw new Error(`${req.walletId} extension returned no account`);
|
|
588
|
-
await
|
|
589
|
-
const chainId =
|
|
673
|
+
await ensureChain({ rpc: rpcFor3(provider), chainId: requestedChain(req) });
|
|
674
|
+
const chainId = toCaip(
|
|
590
675
|
await provider.request({ method: "eth_chainId" })
|
|
591
676
|
);
|
|
592
677
|
this.bindEvents(provider, req.walletId);
|
|
@@ -605,7 +690,7 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
605
690
|
const accounts = await provider.request({ method: "eth_accounts" }).catch(() => []);
|
|
606
691
|
const account = accounts?.[0];
|
|
607
692
|
if (!account) continue;
|
|
608
|
-
const chainId =
|
|
693
|
+
const chainId = toCaip(
|
|
609
694
|
await provider.request({ method: "eth_chainId" }).catch(() => "0x1")
|
|
610
695
|
);
|
|
611
696
|
this.bindEvents(provider, walletId);
|
|
@@ -624,7 +709,7 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
624
709
|
this.emit({
|
|
625
710
|
type: "chainChanged",
|
|
626
711
|
walletId,
|
|
627
|
-
chainId:
|
|
712
|
+
chainId: toCaip(String(hex))
|
|
628
713
|
});
|
|
629
714
|
});
|
|
630
715
|
provider.on("accountsChanged", (accounts) => {
|
|
@@ -637,22 +722,6 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
637
722
|
() => this.emit({ type: "disconnect", walletId })
|
|
638
723
|
);
|
|
639
724
|
}
|
|
640
|
-
/** Best-effort switch to the requested chain; leaves the wallet as-is if it's unknown to it. */
|
|
641
|
-
async ensureChain(provider, req) {
|
|
642
|
-
const wanted = req.namespace.value.chains?.[0];
|
|
643
|
-
const wantedNum = wanted ? Number(wanted.split(":")[1]) : NaN;
|
|
644
|
-
if (!Number.isFinite(wantedNum) || wantedNum <= 0) return;
|
|
645
|
-
const current = parseInt(
|
|
646
|
-
await provider.request({ method: "eth_chainId" }).catch(() => "0x0") ?? "0x0",
|
|
647
|
-
16
|
|
648
|
-
);
|
|
649
|
-
if (current === wantedNum) return;
|
|
650
|
-
await provider.request({
|
|
651
|
-
method: "wallet_switchEthereumChain",
|
|
652
|
-
params: [{ chainId: `0x${wantedNum.toString(16)}` }]
|
|
653
|
-
}).catch(() => {
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
725
|
toConnection(provider, walletId, account, chainId) {
|
|
657
726
|
return {
|
|
658
727
|
walletId,
|
|
@@ -676,29 +745,8 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
676
745
|
};
|
|
677
746
|
}
|
|
678
747
|
};
|
|
679
|
-
function
|
|
680
|
-
|
|
681
|
-
const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
|
|
682
|
-
return `eip155:${Number.isFinite(num) ? num : 1}`;
|
|
683
|
-
}
|
|
684
|
-
function withSignal(promise, signal) {
|
|
685
|
-
if (!signal) return promise;
|
|
686
|
-
if (signal.aborted)
|
|
687
|
-
return Promise.reject(new DOMException("Aborted", "AbortError"));
|
|
688
|
-
return new Promise((resolve, reject) => {
|
|
689
|
-
const onAbort = () => reject(new DOMException("Aborted", "AbortError"));
|
|
690
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
691
|
-
promise.then(
|
|
692
|
-
(value) => {
|
|
693
|
-
signal.removeEventListener("abort", onAbort);
|
|
694
|
-
resolve(value);
|
|
695
|
-
},
|
|
696
|
-
(error) => {
|
|
697
|
-
signal.removeEventListener("abort", onAbort);
|
|
698
|
-
reject(error);
|
|
699
|
-
}
|
|
700
|
-
);
|
|
701
|
-
});
|
|
748
|
+
function rpcFor3(provider) {
|
|
749
|
+
return (args) => provider.request(args);
|
|
702
750
|
}
|
|
703
751
|
|
|
704
752
|
// src/connectors/walletconnect/wallet-links.ts
|
|
@@ -1148,13 +1196,15 @@ function buildLink(base, uri) {
|
|
|
1148
1196
|
const sep = base.includes("?") ? "&" : "?";
|
|
1149
1197
|
return `${base}${sep}uri=${encodeURIComponent(uri)}`;
|
|
1150
1198
|
}
|
|
1151
|
-
function
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
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;
|
|
1158
1208
|
}
|
|
1159
1209
|
function openWalletApp(link) {
|
|
1160
1210
|
if (typeof document === "undefined") return;
|
|
@@ -1193,6 +1243,7 @@ function detectAppOpen(timeoutMs) {
|
|
|
1193
1243
|
|
|
1194
1244
|
// src/connectors/walletconnect/connector.ts
|
|
1195
1245
|
var STORAGE_KEY = "lydianconnect.wc.topics";
|
|
1246
|
+
var PING_TIMEOUT_MS = 3e3;
|
|
1196
1247
|
var WalletConnectConnector = class extends BaseConnector {
|
|
1197
1248
|
constructor(app, options) {
|
|
1198
1249
|
super();
|
|
@@ -1203,6 +1254,9 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1203
1254
|
// claims any wallet not served by an SDK connector
|
|
1204
1255
|
this.client = null;
|
|
1205
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();
|
|
1206
1260
|
this.links = { ...DEFAULT_WALLET_LINKS, ...options.walletLinks ?? {} };
|
|
1207
1261
|
}
|
|
1208
1262
|
isAvailable() {
|
|
@@ -1246,35 +1300,37 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1246
1300
|
}
|
|
1247
1301
|
async connect(req) {
|
|
1248
1302
|
const client = await this.getClient();
|
|
1249
|
-
const reused = this.tryReuse(client, req);
|
|
1303
|
+
const reused = await this.tryReuse(client, req);
|
|
1250
1304
|
if (reused) return reused;
|
|
1251
1305
|
const { uri, approval } = await client.connect({
|
|
1252
1306
|
requiredNamespaces: { [req.namespace.name]: req.namespace.value }
|
|
1253
1307
|
});
|
|
1254
1308
|
if (!uri) throw new Error("WalletConnect did not return a pairing URI");
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
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
|
+
});
|
|
1272
1328
|
}
|
|
1273
1329
|
const pairingTopic = utils.parseUri(uri).topic;
|
|
1274
|
-
const session = await
|
|
1330
|
+
const session = await withSignal(approval(), req.signal, () => {
|
|
1275
1331
|
void client.core.pairing.disconnect({ topic: pairingTopic }).catch(() => {
|
|
1276
1332
|
});
|
|
1277
|
-
});
|
|
1333
|
+
}).finally(() => this.pending.delete(req.walletId));
|
|
1278
1334
|
const connection = this.toConnection(
|
|
1279
1335
|
client,
|
|
1280
1336
|
req.walletId,
|
|
@@ -1285,11 +1341,30 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1285
1341
|
this.persist();
|
|
1286
1342
|
return connection;
|
|
1287
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
|
+
}
|
|
1288
1359
|
async restore() {
|
|
1289
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
|
+
);
|
|
1290
1366
|
const out = [];
|
|
1291
|
-
for (const [walletId,
|
|
1292
|
-
const session = this.liveSession(client, topic);
|
|
1367
|
+
for (const [walletId, session] of checked) {
|
|
1293
1368
|
if (!session) {
|
|
1294
1369
|
this.topics.delete(walletId);
|
|
1295
1370
|
continue;
|
|
@@ -1313,15 +1388,17 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1313
1388
|
);
|
|
1314
1389
|
}
|
|
1315
1390
|
this.topics.clear();
|
|
1391
|
+
this.pending.clear();
|
|
1316
1392
|
this.persist();
|
|
1317
1393
|
}
|
|
1318
1394
|
// --- helpers ---
|
|
1319
|
-
tryReuse(client, req) {
|
|
1395
|
+
async tryReuse(client, req) {
|
|
1320
1396
|
const topic = this.topics.get(req.walletId);
|
|
1321
1397
|
if (!topic) return null;
|
|
1322
|
-
const session = this.liveSession(client, topic);
|
|
1398
|
+
const session = await this.liveSession(client, topic);
|
|
1323
1399
|
if (!session) {
|
|
1324
1400
|
this.topics.delete(req.walletId);
|
|
1401
|
+
this.persist();
|
|
1325
1402
|
return null;
|
|
1326
1403
|
}
|
|
1327
1404
|
const ns = session.namespaces[req.namespace.name];
|
|
@@ -1332,7 +1409,9 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1332
1409
|
toConnection(client, walletId, session, namespace) {
|
|
1333
1410
|
const ns = session.namespaces[namespace.name];
|
|
1334
1411
|
const account = (ns?.accounts ?? []).map((a) => a.split(":")[2]).find((a) => !!a) ?? "";
|
|
1335
|
-
const
|
|
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`;
|
|
1336
1415
|
const topic = session.topic;
|
|
1337
1416
|
return {
|
|
1338
1417
|
walletId,
|
|
@@ -1357,12 +1436,27 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1357
1436
|
}
|
|
1358
1437
|
};
|
|
1359
1438
|
}
|
|
1360
|
-
|
|
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;
|
|
1361
1447
|
try {
|
|
1362
|
-
|
|
1448
|
+
session = client.session.get(topic);
|
|
1363
1449
|
} catch {
|
|
1364
1450
|
return void 0;
|
|
1365
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;
|
|
1366
1460
|
}
|
|
1367
1461
|
walletForTopic(topic) {
|
|
1368
1462
|
for (const [walletId, t] of this.topics) if (t === topic) return walletId;
|
|
@@ -1393,30 +1487,6 @@ function firstNamespace(session) {
|
|
|
1393
1487
|
value: { chains: v.chains, methods: v.methods, events: v.events }
|
|
1394
1488
|
};
|
|
1395
1489
|
}
|
|
1396
|
-
function withSignal2(promise, signal, onAbort) {
|
|
1397
|
-
if (!signal) return promise;
|
|
1398
|
-
if (signal.aborted) {
|
|
1399
|
-
onAbort();
|
|
1400
|
-
return Promise.reject(new DOMException("Aborted", "AbortError"));
|
|
1401
|
-
}
|
|
1402
|
-
return new Promise((resolve, reject) => {
|
|
1403
|
-
const abortHandler = () => {
|
|
1404
|
-
onAbort();
|
|
1405
|
-
reject(new DOMException("Aborted", "AbortError"));
|
|
1406
|
-
};
|
|
1407
|
-
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1408
|
-
promise.then(
|
|
1409
|
-
(v) => {
|
|
1410
|
-
signal.removeEventListener("abort", abortHandler);
|
|
1411
|
-
resolve(v);
|
|
1412
|
-
},
|
|
1413
|
-
(e) => {
|
|
1414
|
-
signal.removeEventListener("abort", abortHandler);
|
|
1415
|
-
reject(e);
|
|
1416
|
-
}
|
|
1417
|
-
);
|
|
1418
|
-
});
|
|
1419
|
-
}
|
|
1420
1490
|
|
|
1421
1491
|
// src/core/lydian-connect.ts
|
|
1422
1492
|
function injectedRdnsMap(sdkServed) {
|
|
@@ -1462,6 +1532,20 @@ var LydianConnect = class {
|
|
|
1462
1532
|
signal: input.signal
|
|
1463
1533
|
});
|
|
1464
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
|
+
}
|
|
1465
1549
|
restore() {
|
|
1466
1550
|
return this.manager.restore();
|
|
1467
1551
|
}
|