@lydianpay/lydianconnect 1.2.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.cjs +251 -160
- 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 +251 -160
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,8 @@ lc.on((event) => {
|
|
|
43
43
|
|
|
44
44
|
`connect({ walletId, chainId })` — `chainId` is the chain the user is paying on (a number like `137`, or a CAIP-2 id like `'eip155:137'`).
|
|
45
45
|
|
|
46
|
+
The WalletConnect client also advertises a return link derived from the page URL (origin + path, no query/hash); wallets may use it to bring the user back to the page after a deep-link approval.
|
|
47
|
+
|
|
46
48
|
## Supported wallets
|
|
47
49
|
|
|
48
50
|
The canonical list of supported wallets lives in
|
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
|
};
|
|
@@ -270,6 +304,84 @@ var BaseConnector = class {
|
|
|
270
304
|
function deriveUrl() {
|
|
271
305
|
return typeof window !== "undefined" ? window.location.origin : "";
|
|
272
306
|
}
|
|
307
|
+
function deriveRedirectUrl() {
|
|
308
|
+
if (typeof window === "undefined") return "";
|
|
309
|
+
return window.location.origin + window.location.pathname;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/connectors/eip155/chain.ts
|
|
313
|
+
var CHAIN_SETTLE = { timeoutMs: 4e3, intervalMs: 100 };
|
|
314
|
+
function toCaip(chainId) {
|
|
315
|
+
if (!chainId) return "eip155:1";
|
|
316
|
+
const num = chainId.startsWith("0x") ? parseInt(chainId, 16) : Number(chainId);
|
|
317
|
+
return `eip155:${Number.isFinite(num) ? num : 1}`;
|
|
318
|
+
}
|
|
319
|
+
async function currentChain(rpc2) {
|
|
320
|
+
const raw = await rpc2({ method: "eth_chainId" }).catch(() => null);
|
|
321
|
+
return toCaip(typeof raw === "string" ? raw : null);
|
|
322
|
+
}
|
|
323
|
+
async function settleChain({
|
|
324
|
+
rpc: rpc2,
|
|
325
|
+
chainId,
|
|
326
|
+
timeoutMs,
|
|
327
|
+
intervalMs
|
|
328
|
+
}) {
|
|
329
|
+
const deadline = Date.now() + timeoutMs;
|
|
330
|
+
for (; ; ) {
|
|
331
|
+
const actual = await currentChain(rpc2);
|
|
332
|
+
if (actual === chainId || Date.now() >= deadline) return;
|
|
333
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async function ensureChain({
|
|
337
|
+
rpc: rpc2,
|
|
338
|
+
chainId,
|
|
339
|
+
timeoutMs = CHAIN_SETTLE.timeoutMs,
|
|
340
|
+
intervalMs = CHAIN_SETTLE.intervalMs
|
|
341
|
+
}) {
|
|
342
|
+
const chainNumber = chainId ? Number(chainId.split(":")[1]) : NaN;
|
|
343
|
+
if (!Number.isFinite(chainNumber) || chainNumber <= 0) return;
|
|
344
|
+
const requested = `eip155:${chainNumber}`;
|
|
345
|
+
if (await currentChain(rpc2) === requested) return;
|
|
346
|
+
const switched = await rpc2({
|
|
347
|
+
method: "wallet_switchEthereumChain",
|
|
348
|
+
params: [{ chainId: `0x${chainNumber.toString(16)}` }]
|
|
349
|
+
}).then(
|
|
350
|
+
() => true,
|
|
351
|
+
// Chain not added to the wallet, or the user declined — the caller's
|
|
352
|
+
// chainChanged will reflect reality. No settle: the chain isn't moving.
|
|
353
|
+
() => false
|
|
354
|
+
);
|
|
355
|
+
if (switched) {
|
|
356
|
+
await settleChain({ rpc: rpc2, chainId: requested, timeoutMs, intervalMs });
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/core/abort.ts
|
|
361
|
+
function withSignal(promise, signal, onAbort) {
|
|
362
|
+
if (!signal) return promise;
|
|
363
|
+
if (signal.aborted) {
|
|
364
|
+
onAbort?.();
|
|
365
|
+
return Promise.reject(new DOMException("Aborted", "AbortError"));
|
|
366
|
+
}
|
|
367
|
+
return new Promise((resolve, reject) => {
|
|
368
|
+
const abortHandler = () => {
|
|
369
|
+
onAbort?.();
|
|
370
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
371
|
+
};
|
|
372
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
373
|
+
promise.then(
|
|
374
|
+
(value) => {
|
|
375
|
+
signal.removeEventListener("abort", abortHandler);
|
|
376
|
+
resolve(value);
|
|
377
|
+
},
|
|
378
|
+
(error) => {
|
|
379
|
+
signal.removeEventListener("abort", abortHandler);
|
|
380
|
+
reject(error);
|
|
381
|
+
}
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
273
385
|
|
|
274
386
|
// src/connectors/metamask/connector.ts
|
|
275
387
|
var WALLET_ID = "metamask";
|
|
@@ -328,17 +440,23 @@ var MetaMaskConnector = class extends BaseConnector {
|
|
|
328
440
|
);
|
|
329
441
|
}
|
|
330
442
|
async connect(req) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
443
|
+
return this.duringConnect(async () => {
|
|
444
|
+
const provider = await this.getProvider();
|
|
445
|
+
const accounts = await withSignal(
|
|
446
|
+
provider.request({ method: "eth_requestAccounts" }),
|
|
447
|
+
req.signal
|
|
448
|
+
);
|
|
449
|
+
const account = accounts?.[0];
|
|
450
|
+
if (!account) throw new Error("MetaMask returned no account");
|
|
451
|
+
await ensureChain({
|
|
452
|
+
rpc: rpcFor(provider),
|
|
453
|
+
chainId: requestedChain(req)
|
|
454
|
+
});
|
|
455
|
+
const chainId = toCaip(
|
|
456
|
+
await provider.request({ method: "eth_chainId" })
|
|
457
|
+
);
|
|
458
|
+
return this.toConnection(provider, account, chainId);
|
|
334
459
|
});
|
|
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
460
|
}
|
|
343
461
|
async restore() {
|
|
344
462
|
const provider = await this.getProvider().catch(() => null);
|
|
@@ -356,22 +474,6 @@ var MetaMaskConnector = class extends BaseConnector {
|
|
|
356
474
|
this.provider = null;
|
|
357
475
|
this.listenersBound = false;
|
|
358
476
|
}
|
|
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
477
|
toConnection(provider, account, chainId) {
|
|
376
478
|
return {
|
|
377
479
|
walletId: WALLET_ID,
|
|
@@ -393,20 +495,18 @@ var MetaMaskConnector = class extends BaseConnector {
|
|
|
393
495
|
};
|
|
394
496
|
}
|
|
395
497
|
};
|
|
396
|
-
function
|
|
397
|
-
|
|
398
|
-
const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
|
|
399
|
-
return `eip155:${Number.isFinite(num) ? num : 1}`;
|
|
498
|
+
function rpcFor(provider) {
|
|
499
|
+
return (args) => provider.request(args);
|
|
400
500
|
}
|
|
401
501
|
|
|
402
502
|
// src/connectors/coinbase/connector.ts
|
|
503
|
+
var WALLET_ID2 = "coinbase";
|
|
403
504
|
function rpc(provider, method, params) {
|
|
404
505
|
return provider.request({
|
|
405
506
|
method,
|
|
406
507
|
params
|
|
407
508
|
});
|
|
408
509
|
}
|
|
409
|
-
var WALLET_ID2 = "coinbase";
|
|
410
510
|
var CoinbaseConnector = class extends BaseConnector {
|
|
411
511
|
constructor(app) {
|
|
412
512
|
super();
|
|
@@ -443,7 +543,7 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
443
543
|
this.emit({
|
|
444
544
|
type: "chainChanged",
|
|
445
545
|
walletId: WALLET_ID2,
|
|
446
|
-
chainId:
|
|
546
|
+
chainId: toCaip(String(hex))
|
|
447
547
|
});
|
|
448
548
|
});
|
|
449
549
|
provider.on("accountsChanged", (accounts) => {
|
|
@@ -458,13 +558,18 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
458
558
|
);
|
|
459
559
|
}
|
|
460
560
|
async connect(req) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
561
|
+
return this.duringConnect(async () => {
|
|
562
|
+
const provider = await this.getProvider(evmChainIds(req));
|
|
563
|
+
const accounts = await rpc(provider, "eth_requestAccounts");
|
|
564
|
+
const account = accounts?.[0];
|
|
565
|
+
if (!account) throw new Error("Coinbase Wallet returned no account");
|
|
566
|
+
await ensureChain({
|
|
567
|
+
rpc: rpcFor2(provider),
|
|
568
|
+
chainId: requestedChain(req)
|
|
569
|
+
});
|
|
570
|
+
const chainId = toCaip(await rpc(provider, "eth_chainId"));
|
|
571
|
+
return this.toConnection(provider, account, chainId);
|
|
572
|
+
});
|
|
468
573
|
}
|
|
469
574
|
async restore() {
|
|
470
575
|
const provider = await this.getProvider().catch(() => null);
|
|
@@ -474,7 +579,7 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
474
579
|
);
|
|
475
580
|
const account = accounts?.[0];
|
|
476
581
|
if (!account) return [];
|
|
477
|
-
const chainId =
|
|
582
|
+
const chainId = toCaip(
|
|
478
583
|
await rpc(provider, "eth_chainId").catch(() => "0x1")
|
|
479
584
|
);
|
|
480
585
|
return [this.toConnection(provider, account, chainId)];
|
|
@@ -485,20 +590,6 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
485
590
|
this.provider = null;
|
|
486
591
|
this.listenersBound = false;
|
|
487
592
|
}
|
|
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
593
|
toConnection(provider, account, chainId) {
|
|
503
594
|
return {
|
|
504
595
|
walletId: WALLET_ID2,
|
|
@@ -518,10 +609,8 @@ var CoinbaseConnector = class extends BaseConnector {
|
|
|
518
609
|
};
|
|
519
610
|
}
|
|
520
611
|
};
|
|
521
|
-
function
|
|
522
|
-
|
|
523
|
-
const num = hexChainId.startsWith("0x") ? parseInt(hexChainId, 16) : Number(hexChainId);
|
|
524
|
-
return `eip155:${Number.isFinite(num) ? num : 1}`;
|
|
612
|
+
function rpcFor2(provider) {
|
|
613
|
+
return ({ method, params }) => rpc(provider, method, params);
|
|
525
614
|
}
|
|
526
615
|
function evmChainIds(req) {
|
|
527
616
|
const wanted = req.namespace.value.chains?.[0];
|
|
@@ -585,8 +674,8 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
585
674
|
const account = accounts?.[0];
|
|
586
675
|
if (!account)
|
|
587
676
|
throw new Error(`${req.walletId} extension returned no account`);
|
|
588
|
-
await
|
|
589
|
-
const chainId =
|
|
677
|
+
await ensureChain({ rpc: rpcFor3(provider), chainId: requestedChain(req) });
|
|
678
|
+
const chainId = toCaip(
|
|
590
679
|
await provider.request({ method: "eth_chainId" })
|
|
591
680
|
);
|
|
592
681
|
this.bindEvents(provider, req.walletId);
|
|
@@ -605,7 +694,7 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
605
694
|
const accounts = await provider.request({ method: "eth_accounts" }).catch(() => []);
|
|
606
695
|
const account = accounts?.[0];
|
|
607
696
|
if (!account) continue;
|
|
608
|
-
const chainId =
|
|
697
|
+
const chainId = toCaip(
|
|
609
698
|
await provider.request({ method: "eth_chainId" }).catch(() => "0x1")
|
|
610
699
|
);
|
|
611
700
|
this.bindEvents(provider, walletId);
|
|
@@ -624,7 +713,7 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
624
713
|
this.emit({
|
|
625
714
|
type: "chainChanged",
|
|
626
715
|
walletId,
|
|
627
|
-
chainId:
|
|
716
|
+
chainId: toCaip(String(hex))
|
|
628
717
|
});
|
|
629
718
|
});
|
|
630
719
|
provider.on("accountsChanged", (accounts) => {
|
|
@@ -637,22 +726,6 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
637
726
|
() => this.emit({ type: "disconnect", walletId })
|
|
638
727
|
);
|
|
639
728
|
}
|
|
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
729
|
toConnection(provider, walletId, account, chainId) {
|
|
657
730
|
return {
|
|
658
731
|
walletId,
|
|
@@ -676,29 +749,8 @@ var InjectedConnector = class extends BaseConnector {
|
|
|
676
749
|
};
|
|
677
750
|
}
|
|
678
751
|
};
|
|
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
|
-
});
|
|
752
|
+
function rpcFor3(provider) {
|
|
753
|
+
return (args) => provider.request(args);
|
|
702
754
|
}
|
|
703
755
|
|
|
704
756
|
// src/connectors/walletconnect/wallet-links.ts
|
|
@@ -1148,13 +1200,15 @@ function buildLink(base, uri) {
|
|
|
1148
1200
|
const sep = base.includes("?") ? "&" : "?";
|
|
1149
1201
|
return `${base}${sep}uri=${encodeURIComponent(uri)}`;
|
|
1150
1202
|
}
|
|
1151
|
-
function
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1203
|
+
function canOpen(link) {
|
|
1204
|
+
return !!(link?.scheme || link?.universal);
|
|
1205
|
+
}
|
|
1206
|
+
function openWallet(link, uri, via = "scheme") {
|
|
1207
|
+
if (typeof document === "undefined") return false;
|
|
1208
|
+
const base = via === "universal" ? link.universal : link.scheme ?? link.universal;
|
|
1209
|
+
if (!base) return false;
|
|
1210
|
+
openHref(buildLink(base, uri));
|
|
1211
|
+
return true;
|
|
1158
1212
|
}
|
|
1159
1213
|
function openWalletApp(link) {
|
|
1160
1214
|
if (typeof document === "undefined") return;
|
|
@@ -1193,6 +1247,7 @@ function detectAppOpen(timeoutMs) {
|
|
|
1193
1247
|
|
|
1194
1248
|
// src/connectors/walletconnect/connector.ts
|
|
1195
1249
|
var STORAGE_KEY = "lydianconnect.wc.topics";
|
|
1250
|
+
var PING_TIMEOUT_MS = 3e3;
|
|
1196
1251
|
var WalletConnectConnector = class extends BaseConnector {
|
|
1197
1252
|
constructor(app, options) {
|
|
1198
1253
|
super();
|
|
@@ -1203,6 +1258,9 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1203
1258
|
// claims any wallet not served by an SDK connector
|
|
1204
1259
|
this.client = null;
|
|
1205
1260
|
this.topics = /* @__PURE__ */ new Map();
|
|
1261
|
+
/** walletId -> pairing URI of the connect currently awaiting approval, so a
|
|
1262
|
+
* host-driven open button can fire without the host tracking the URI itself. */
|
|
1263
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
1206
1264
|
this.links = { ...DEFAULT_WALLET_LINKS, ...options.walletLinks ?? {} };
|
|
1207
1265
|
}
|
|
1208
1266
|
isAvailable() {
|
|
@@ -1210,13 +1268,16 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1210
1268
|
}
|
|
1211
1269
|
async getClient() {
|
|
1212
1270
|
if (this.client) return this.client;
|
|
1271
|
+
const redirectUrl = deriveRedirectUrl();
|
|
1213
1272
|
const client = await SignClient__default.default.init({
|
|
1214
1273
|
projectId: this.options.projectId,
|
|
1215
1274
|
metadata: {
|
|
1216
1275
|
name: this.app.appName,
|
|
1217
1276
|
description: this.app.description ?? this.app.appName,
|
|
1218
1277
|
url: deriveUrl(),
|
|
1219
|
-
icons: this.app.icon ? [this.app.icon] : []
|
|
1278
|
+
icons: this.app.icon ? [this.app.icon] : [],
|
|
1279
|
+
// Wallets may use this to return the user after deep-link approvals.
|
|
1280
|
+
...redirectUrl ? { redirect: { universal: redirectUrl } } : {}
|
|
1220
1281
|
},
|
|
1221
1282
|
relayUrl: this.options.relayUrl
|
|
1222
1283
|
});
|
|
@@ -1246,35 +1307,37 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1246
1307
|
}
|
|
1247
1308
|
async connect(req) {
|
|
1248
1309
|
const client = await this.getClient();
|
|
1249
|
-
const reused = this.tryReuse(client, req);
|
|
1310
|
+
const reused = await this.tryReuse(client, req);
|
|
1250
1311
|
if (reused) return reused;
|
|
1251
1312
|
const { uri, approval } = await client.connect({
|
|
1252
1313
|
requiredNamespaces: { [req.namespace.name]: req.namespace.value }
|
|
1253
1314
|
});
|
|
1254
1315
|
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
|
-
|
|
1316
|
+
const link = isMobile() ? this.links[req.walletId] : void 0;
|
|
1317
|
+
this.emit({
|
|
1318
|
+
type: "display_uri",
|
|
1319
|
+
walletId: req.walletId,
|
|
1320
|
+
uri,
|
|
1321
|
+
deepLink: canOpen(link)
|
|
1322
|
+
});
|
|
1323
|
+
if (link && canOpen(link)) {
|
|
1324
|
+
this.pending.set(req.walletId, uri);
|
|
1325
|
+
openWallet(link, uri);
|
|
1326
|
+
void detectAppOpen(this.options.openTimeoutMs ?? 2e3).then((opened) => {
|
|
1327
|
+
if (!opened)
|
|
1328
|
+
this.emit({
|
|
1329
|
+
type: "wallet_open_failed",
|
|
1330
|
+
walletId: req.walletId,
|
|
1331
|
+
uri,
|
|
1332
|
+
store: link.store
|
|
1333
|
+
});
|
|
1334
|
+
});
|
|
1272
1335
|
}
|
|
1273
1336
|
const pairingTopic = utils.parseUri(uri).topic;
|
|
1274
|
-
const session = await
|
|
1337
|
+
const session = await withSignal(approval(), req.signal, () => {
|
|
1275
1338
|
void client.core.pairing.disconnect({ topic: pairingTopic }).catch(() => {
|
|
1276
1339
|
});
|
|
1277
|
-
});
|
|
1340
|
+
}).finally(() => this.pending.delete(req.walletId));
|
|
1278
1341
|
const connection = this.toConnection(
|
|
1279
1342
|
client,
|
|
1280
1343
|
req.walletId,
|
|
@@ -1285,11 +1348,30 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1285
1348
|
this.persist();
|
|
1286
1349
|
return connection;
|
|
1287
1350
|
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Fire a wallet's deep link on demand. The host calls this from a real click,
|
|
1353
|
+
* which is the one context a mobile browser reliably allows a scheme
|
|
1354
|
+
* navigation from — see the note on {@link openWallet}.
|
|
1355
|
+
*/
|
|
1356
|
+
openWallet(input) {
|
|
1357
|
+
const link = this.links[input.walletId];
|
|
1358
|
+
if (!link) return false;
|
|
1359
|
+
const uri = input.uri ?? this.pending.get(input.walletId);
|
|
1360
|
+
if (!uri) {
|
|
1361
|
+
openWalletApp(link);
|
|
1362
|
+
return !!link.native;
|
|
1363
|
+
}
|
|
1364
|
+
return openWallet(link, uri, input.via ?? "scheme");
|
|
1365
|
+
}
|
|
1288
1366
|
async restore() {
|
|
1289
1367
|
const client = await this.getClient();
|
|
1368
|
+
const checked = await Promise.all(
|
|
1369
|
+
[...this.topics].map(
|
|
1370
|
+
async ([walletId, topic]) => [walletId, await this.liveSession(client, topic)]
|
|
1371
|
+
)
|
|
1372
|
+
);
|
|
1290
1373
|
const out = [];
|
|
1291
|
-
for (const [walletId,
|
|
1292
|
-
const session = this.liveSession(client, topic);
|
|
1374
|
+
for (const [walletId, session] of checked) {
|
|
1293
1375
|
if (!session) {
|
|
1294
1376
|
this.topics.delete(walletId);
|
|
1295
1377
|
continue;
|
|
@@ -1313,15 +1395,17 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1313
1395
|
);
|
|
1314
1396
|
}
|
|
1315
1397
|
this.topics.clear();
|
|
1398
|
+
this.pending.clear();
|
|
1316
1399
|
this.persist();
|
|
1317
1400
|
}
|
|
1318
1401
|
// --- helpers ---
|
|
1319
|
-
tryReuse(client, req) {
|
|
1402
|
+
async tryReuse(client, req) {
|
|
1320
1403
|
const topic = this.topics.get(req.walletId);
|
|
1321
1404
|
if (!topic) return null;
|
|
1322
|
-
const session = this.liveSession(client, topic);
|
|
1405
|
+
const session = await this.liveSession(client, topic);
|
|
1323
1406
|
if (!session) {
|
|
1324
1407
|
this.topics.delete(req.walletId);
|
|
1408
|
+
this.persist();
|
|
1325
1409
|
return null;
|
|
1326
1410
|
}
|
|
1327
1411
|
const ns = session.namespaces[req.namespace.name];
|
|
@@ -1332,7 +1416,9 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1332
1416
|
toConnection(client, walletId, session, namespace) {
|
|
1333
1417
|
const ns = session.namespaces[namespace.name];
|
|
1334
1418
|
const account = (ns?.accounts ?? []).map((a) => a.split(":")[2]).find((a) => !!a) ?? "";
|
|
1335
|
-
const
|
|
1419
|
+
const approved = ns?.chains?.length ? ns.chains : (ns?.accounts ?? []).map((a) => a.split(":").slice(0, 2).join(":"));
|
|
1420
|
+
const wanted = namespace.value.chains?.[0];
|
|
1421
|
+
const chainId = wanted && approved.includes(wanted) ? wanted : approved[0] ?? `${namespace.name}:1`;
|
|
1336
1422
|
const topic = session.topic;
|
|
1337
1423
|
return {
|
|
1338
1424
|
walletId,
|
|
@@ -1357,12 +1443,27 @@ var WalletConnectConnector = class extends BaseConnector {
|
|
|
1357
1443
|
}
|
|
1358
1444
|
};
|
|
1359
1445
|
}
|
|
1360
|
-
|
|
1446
|
+
/**
|
|
1447
|
+
* A stored session the wallet is still on the other end of. The local record
|
|
1448
|
+
* is not evidence — it survives the user clearing the dApp inside their
|
|
1449
|
+
* wallet — and reusing a dead one returns a Connection that looks healthy
|
|
1450
|
+
* while every request hangs. So the peer has to answer for it.
|
|
1451
|
+
*/
|
|
1452
|
+
async liveSession(client, topic) {
|
|
1453
|
+
let session;
|
|
1361
1454
|
try {
|
|
1362
|
-
|
|
1455
|
+
session = client.session.get(topic);
|
|
1363
1456
|
} catch {
|
|
1364
1457
|
return void 0;
|
|
1365
1458
|
}
|
|
1459
|
+
let timer;
|
|
1460
|
+
const alive = await Promise.race([
|
|
1461
|
+
client.ping({ topic }).then(() => true).catch(() => false),
|
|
1462
|
+
new Promise((resolve) => {
|
|
1463
|
+
timer = setTimeout(() => resolve(false), PING_TIMEOUT_MS);
|
|
1464
|
+
})
|
|
1465
|
+
]).finally(() => clearTimeout(timer));
|
|
1466
|
+
return alive ? session : void 0;
|
|
1366
1467
|
}
|
|
1367
1468
|
walletForTopic(topic) {
|
|
1368
1469
|
for (const [walletId, t] of this.topics) if (t === topic) return walletId;
|
|
@@ -1393,30 +1494,6 @@ function firstNamespace(session) {
|
|
|
1393
1494
|
value: { chains: v.chains, methods: v.methods, events: v.events }
|
|
1394
1495
|
};
|
|
1395
1496
|
}
|
|
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
1497
|
|
|
1421
1498
|
// src/core/lydian-connect.ts
|
|
1422
1499
|
function injectedRdnsMap(sdkServed) {
|
|
@@ -1462,6 +1539,20 @@ var LydianConnect = class {
|
|
|
1462
1539
|
signal: input.signal
|
|
1463
1540
|
});
|
|
1464
1541
|
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Open a wallet app, optionally handing it the pairing URI from `display_uri`.
|
|
1544
|
+
*
|
|
1545
|
+
* Call this from a real click handler. A mobile browser will suppress the same
|
|
1546
|
+
* navigation issued automatically after an async step, which is why the
|
|
1547
|
+
* `display_uri` event reports `deepLink` — that is the library asking the host
|
|
1548
|
+
* for a button to put this behind.
|
|
1549
|
+
*
|
|
1550
|
+
* Returns whether a link was fired. `false` means nothing is known for this
|
|
1551
|
+
* wallet on this device; show the QR code instead.
|
|
1552
|
+
*/
|
|
1553
|
+
openWallet(input) {
|
|
1554
|
+
return this.manager.openWallet(input);
|
|
1555
|
+
}
|
|
1465
1556
|
restore() {
|
|
1466
1557
|
return this.manager.restore();
|
|
1467
1558
|
}
|