@bwalletx/connect 0.5.4 → 0.5.6
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/CHANGELOG.md +28 -0
- package/README.md +76 -8
- package/dist/bwalletx-connect.global.js +92 -25
- package/dist/bwalletx-connect.global.js.map +1 -1
- package/dist/{wallets-CbzMtxrf.d.ts → choice-DCh-tgpT.d.cts} +112 -4
- package/dist/{wallets-CbzMtxrf.d.cts → choice-DCh-tgpT.d.ts} +112 -4
- package/dist/chunk-34DKBQNN.js +222 -0
- package/dist/chunk-34DKBQNN.js.map +1 -0
- package/dist/{chunk-QRXFL66W.js → chunk-A2WLGS25.js} +6 -4
- package/dist/chunk-A2WLGS25.js.map +1 -0
- package/dist/chunk-IFGOAROM.js +255 -0
- package/dist/chunk-IFGOAROM.js.map +1 -0
- package/dist/chunk-LS6AYEBO.js +1886 -0
- package/dist/chunk-LS6AYEBO.js.map +1 -0
- package/dist/element-D9Zl-x12.d.cts +46 -0
- package/dist/element-D9Zl-x12.d.ts +46 -0
- package/dist/element.cjs +1147 -61
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +23 -28
- package/dist/element.d.ts +23 -28
- package/dist/element.js +29 -2
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +1469 -453
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +163 -14
- package/dist/index.d.ts +163 -14
- package/dist/index.js +100 -12
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +5 -3
- package/dist/next.cjs.map +1 -1
- package/dist/next.js +1 -1
- package/dist/react.cjs +994 -183
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +75 -8
- package/dist/react.d.ts +75 -8
- package/dist/react.js +121 -40
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +5 -3
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +8 -3
- package/dist/server.d.ts +8 -3
- package/dist/server.js +1 -1
- package/package.json +10 -9
- package/dist/chunk-EXRQJTXF.js +0 -910
- package/dist/chunk-EXRQJTXF.js.map +0 -1
- package/dist/chunk-QRXFL66W.js.map +0 -1
- package/dist/chunk-RIOHG5JH.js +0 -459
- package/dist/chunk-RIOHG5JH.js.map +0 -1
package/dist/react.cjs
CHANGED
|
@@ -34,12 +34,16 @@ __export(react_exports, {
|
|
|
34
34
|
BwalletxProvider: () => BwalletxProvider,
|
|
35
35
|
BwalletxSignin: () => BwalletxSignin,
|
|
36
36
|
PaymentError: () => PaymentError,
|
|
37
|
+
accountFor: () => accountFor,
|
|
38
|
+
autoChoice: () => autoChoice,
|
|
37
39
|
brc100Option: () => brc100Option,
|
|
38
40
|
default: () => react_default,
|
|
39
41
|
discoverWallets: () => discoverWallets,
|
|
40
42
|
handcashAdapter: () => handcashAdapter,
|
|
41
43
|
pairingOption: () => pairingOption,
|
|
42
|
-
|
|
44
|
+
signinStateFor: () => signinStateFor,
|
|
45
|
+
useBwalletX: () => useBwalletX,
|
|
46
|
+
useBwalletXAccount: () => useBwalletXAccount
|
|
43
47
|
});
|
|
44
48
|
module.exports = __toCommonJS(react_exports);
|
|
45
49
|
var import_react = require("react");
|
|
@@ -446,6 +450,73 @@ function declareWalletTheme(theme) {
|
|
|
446
450
|
if (meta.content !== id) meta.content = id;
|
|
447
451
|
}
|
|
448
452
|
|
|
453
|
+
// src/account.ts
|
|
454
|
+
var PAYMAIL_LOOKUP_URL = "https://pay.bwallet.space/api/paymail/lookup?key=";
|
|
455
|
+
var BAVATAR_URL = "https://chat.bwalletx.com/api/bavatar/";
|
|
456
|
+
function shortKey(key) {
|
|
457
|
+
return key.length > 12 ? `${key.slice(0, 6)}\u2026${key.slice(-4)}` : key;
|
|
458
|
+
}
|
|
459
|
+
function accountLabel(a) {
|
|
460
|
+
if (a.paymail && /@bwalletx\.com$/i.test(a.paymail)) return "$" + a.paymail.split("@")[0];
|
|
461
|
+
if (a.handle) return a.handle;
|
|
462
|
+
if (a.paymail) return a.paymail;
|
|
463
|
+
return a.identityKey ? shortKey(a.identityKey) : "";
|
|
464
|
+
}
|
|
465
|
+
function avatarUrlFor(identityKey) {
|
|
466
|
+
return identityKey && /^0[23][0-9a-fA-F]{64}$/.test(identityKey) ? `${BAVATAR_URL}${identityKey.toLowerCase()}.png` : null;
|
|
467
|
+
}
|
|
468
|
+
var cache = /* @__PURE__ */ new Map();
|
|
469
|
+
function lookupPaymail(identityKey, url = PAYMAIL_LOOKUP_URL) {
|
|
470
|
+
if (url === "off" || typeof fetch === "undefined") return Promise.resolve(null);
|
|
471
|
+
const k = `${url}|${identityKey}`;
|
|
472
|
+
let p = cache.get(k);
|
|
473
|
+
if (!p) {
|
|
474
|
+
p = fetch(url + encodeURIComponent(identityKey)).then((r) => r.ok ? r.json() : null).then((j) => j && typeof j.paymail === "string" ? j.paymail : null).catch(() => null);
|
|
475
|
+
cache.set(k, p);
|
|
476
|
+
}
|
|
477
|
+
return p;
|
|
478
|
+
}
|
|
479
|
+
function isUserRejection(e) {
|
|
480
|
+
const code = e?.code;
|
|
481
|
+
if (code === "ERR_PERMISSION_DENIED" || code === 4001) return true;
|
|
482
|
+
const msg = e instanceof Error ? e.message : String(e ?? "");
|
|
483
|
+
return /denied|reject|cancel|declin|refused|not now|user closed/i.test(msg);
|
|
484
|
+
}
|
|
485
|
+
function signInErrorText(e) {
|
|
486
|
+
if (isUserRejection(e)) return "Sign-in cancelled";
|
|
487
|
+
const code = e?.code;
|
|
488
|
+
if (code === "NOT_FOUND") return "bWalletX was not found in this browser";
|
|
489
|
+
const msg = e instanceof Error ? e.message : String(e ?? "");
|
|
490
|
+
if (/lock/i.test(msg)) return "Your wallet is locked. Unlock it and try again";
|
|
491
|
+
if (/timeout|timed out|expired/i.test(msg)) return "The wallet did not answer in time";
|
|
492
|
+
if (/origin/i.test(msg)) return "This page is not on the site\u2019s configured origin";
|
|
493
|
+
return "Sign-in failed";
|
|
494
|
+
}
|
|
495
|
+
async function copyText(text) {
|
|
496
|
+
try {
|
|
497
|
+
await navigator.clipboard.writeText(text);
|
|
498
|
+
return true;
|
|
499
|
+
} catch {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
var IDENTITY_HINT_KEY = "bwx:identityKey";
|
|
504
|
+
function rememberedIdentity() {
|
|
505
|
+
try {
|
|
506
|
+
const k = globalThis.localStorage?.getItem(IDENTITY_HINT_KEY) ?? "";
|
|
507
|
+
return /^0[23][0-9a-f]{64}$/i.test(k) ? k : void 0;
|
|
508
|
+
} catch {
|
|
509
|
+
return void 0;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
function rememberIdentity(key) {
|
|
513
|
+
try {
|
|
514
|
+
if (key) globalThis.localStorage?.setItem(IDENTITY_HINT_KEY, key);
|
|
515
|
+
else globalThis.localStorage?.removeItem(IDENTITY_HINT_KEY);
|
|
516
|
+
} catch {
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
449
520
|
// src/discover.ts
|
|
450
521
|
var import_sdk3 = require("@bsv/sdk");
|
|
451
522
|
var RDNS_EXTENSION = "com.bwalletx.extension";
|
|
@@ -471,21 +542,16 @@ function discoverBwalletX(ms = DEFAULT_DISCOVER_MS) {
|
|
|
471
542
|
finish({ wallet: d.wallet, method: rdns === RDNS_EXTENSION ? "extension" : "in-app", rdns });
|
|
472
543
|
}
|
|
473
544
|
};
|
|
474
|
-
const cwiNow = () => {
|
|
475
|
-
const cwi = window.CWI;
|
|
476
|
-
if (cwi) finish({ wallet: cwi, method: "in-app", rdns: null });
|
|
477
|
-
return !!cwi;
|
|
478
|
-
};
|
|
479
545
|
window.addEventListener("brc100:announceWallet", onAnnounce);
|
|
480
546
|
window.dispatchEvent(new Event("brc100:requestWallet"));
|
|
481
|
-
if (done
|
|
547
|
+
if (done) return;
|
|
482
548
|
if (isFramed()) {
|
|
483
549
|
void probeXdm(Math.max(ms, 1200)).then((w) => finish(w ? { wallet: w, method: "in-app", rdns: null } : null));
|
|
484
550
|
return;
|
|
485
551
|
}
|
|
486
552
|
timer = setTimeout(() => {
|
|
487
553
|
timer = void 0;
|
|
488
|
-
|
|
554
|
+
finish(null);
|
|
489
555
|
}, ms);
|
|
490
556
|
});
|
|
491
557
|
}
|
|
@@ -589,27 +655,68 @@ async function connectBwalletX(opts = {}) {
|
|
|
589
655
|
status("pairing");
|
|
590
656
|
return { wallet: await pairBwalletX(opts.pairing), method: "pairing" };
|
|
591
657
|
}
|
|
658
|
+
async function waitIfLocked(wallet) {
|
|
659
|
+
let ok = false;
|
|
660
|
+
try {
|
|
661
|
+
ok = !!(await wallet.isAuthenticated({}))?.authenticated;
|
|
662
|
+
} catch {
|
|
663
|
+
}
|
|
664
|
+
if (!ok) await Promise.resolve().then(() => wallet.waitForAuthentication({})).catch(() => {
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
function challengeWantsIdentityKey(e) {
|
|
668
|
+
const msg = e instanceof Error ? e.message : String(e ?? "");
|
|
669
|
+
return /identityKey/i.test(msg);
|
|
670
|
+
}
|
|
592
671
|
async function signInWithBwalletX(opts) {
|
|
593
672
|
const { wallet, method } = await connectBwalletX(opts);
|
|
594
|
-
if (method !== "pairing") await wallet
|
|
673
|
+
if (method !== "pairing" && !opts.skipWaitForAuthentication) await waitIfLocked(wallet);
|
|
674
|
+
const status = () => {
|
|
675
|
+
try {
|
|
676
|
+
opts.onStatus?.("signing");
|
|
677
|
+
} catch {
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
const sign = (c2) => wallet.createSignature({
|
|
681
|
+
protocolID: c2.protocolID ?? LOGIN_PROTOCOL,
|
|
682
|
+
keyID: c2.keyID,
|
|
683
|
+
counterparty: "anyone",
|
|
684
|
+
data: Array.from(new TextEncoder().encode(c2.message))
|
|
595
685
|
});
|
|
596
|
-
const
|
|
686
|
+
const keyFirst = async () => {
|
|
687
|
+
const { publicKey: identityKey2 } = await atStage("identity", () => wallet.getPublicKey({ identityKey: true }));
|
|
688
|
+
status();
|
|
689
|
+
const c2 = await atStage("challenge", () => opts.challenge(identityKey2));
|
|
690
|
+
const { signature: signature2 } = await atStage("signature", () => sign(c2));
|
|
691
|
+
return { identityKey: identityKey2, signature: Array.from(signature2), nonce: c2.nonce, method, wallet };
|
|
692
|
+
};
|
|
693
|
+
if (opts.challengeNeedsIdentityKey && !opts.identityKey) return keyFirst();
|
|
694
|
+
let c;
|
|
597
695
|
try {
|
|
598
|
-
|
|
599
|
-
} catch {
|
|
696
|
+
c = await atStage("challenge", () => opts.challenge(opts.identityKey));
|
|
697
|
+
} catch (e) {
|
|
698
|
+
if (!opts.identityKey && challengeWantsIdentityKey(e)) return keyFirst();
|
|
699
|
+
throw e;
|
|
600
700
|
}
|
|
601
|
-
|
|
602
|
-
const { signature } = await
|
|
603
|
-
"
|
|
604
|
-
() =>
|
|
605
|
-
|
|
606
|
-
keyID: c.keyID,
|
|
607
|
-
counterparty: "anyone",
|
|
608
|
-
data: Array.from(new TextEncoder().encode(c.message))
|
|
609
|
-
})
|
|
610
|
-
);
|
|
701
|
+
status();
|
|
702
|
+
const [{ publicKey: identityKey }, { signature }] = await Promise.all([
|
|
703
|
+
opts.identityKey ? Promise.resolve({ publicKey: opts.identityKey }) : atStage("identity", () => wallet.getPublicKey({ identityKey: true })),
|
|
704
|
+
atStage("signature", () => sign(c))
|
|
705
|
+
]);
|
|
611
706
|
return { identityKey, signature: Array.from(signature), nonce: c.nonce, method, wallet };
|
|
612
707
|
}
|
|
708
|
+
async function signInAndVerify(opts) {
|
|
709
|
+
const hinted = !opts.identityKey && !!opts.identityKeyHint;
|
|
710
|
+
const r = await signInWithBwalletX({ ...opts, identityKey: opts.identityKey ?? opts.identityKeyHint });
|
|
711
|
+
try {
|
|
712
|
+
return { result: r, verified: await atStage("verify", () => opts.verify(r)) };
|
|
713
|
+
} catch (e) {
|
|
714
|
+
const status = e?.status;
|
|
715
|
+
if (!hinted || status !== 401) throw e;
|
|
716
|
+
const again = await signInWithBwalletX({ ...opts, wallet: r.wallet, identityKey: void 0, skipWaitForAuthentication: true });
|
|
717
|
+
return { result: again, verified: await atStage("verify", () => opts.verify(again)) };
|
|
718
|
+
}
|
|
719
|
+
}
|
|
613
720
|
async function atStage(stage, run) {
|
|
614
721
|
try {
|
|
615
722
|
return await run();
|
|
@@ -620,11 +727,272 @@ async function atStage(stage, run) {
|
|
|
620
727
|
}
|
|
621
728
|
}
|
|
622
729
|
|
|
730
|
+
// src/wallets.ts
|
|
731
|
+
var import_sdk4 = require("@bsv/sdk");
|
|
732
|
+
async function brc100Connected(wallet, o, c = {}) {
|
|
733
|
+
if (c.identity === false) return { wallet, payer: wallet, identityKey: null, optionId: o.id, kind: o.kind };
|
|
734
|
+
await waitIfLocked(wallet);
|
|
735
|
+
const { publicKey } = await wallet.getPublicKey({ identityKey: true });
|
|
736
|
+
return { wallet, payer: wallet, identityKey: publicKey, optionId: o.id, kind: o.kind };
|
|
737
|
+
}
|
|
738
|
+
function brc100Option(wallet, info) {
|
|
739
|
+
const opt = {
|
|
740
|
+
id: info.id,
|
|
741
|
+
name: info.name,
|
|
742
|
+
icon: info.icon,
|
|
743
|
+
rdns: info.rdns,
|
|
744
|
+
kind: "brc100",
|
|
745
|
+
isBwalletX: info.isBwalletX ?? false,
|
|
746
|
+
substrate: info.substrate,
|
|
747
|
+
wallet,
|
|
748
|
+
connect: (c) => brc100Connected(wallet, opt, c)
|
|
749
|
+
};
|
|
750
|
+
return opt;
|
|
751
|
+
}
|
|
752
|
+
function pairingOption(pairing) {
|
|
753
|
+
const opt = {
|
|
754
|
+
id: "bwalletx-pairing",
|
|
755
|
+
name: "bWalletX on your phone",
|
|
756
|
+
kind: "pairing",
|
|
757
|
+
isBwalletX: true,
|
|
758
|
+
substrate: "pairing",
|
|
759
|
+
async connect(c) {
|
|
760
|
+
const p = typeof pairing === "function" ? pairing() : pairing;
|
|
761
|
+
const w = await restorePairing(p) ?? await pairBwalletX(p);
|
|
762
|
+
return brc100Connected(w, opt, c);
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
return opt;
|
|
766
|
+
}
|
|
767
|
+
function handcashAdapter(o) {
|
|
768
|
+
const opt = {
|
|
769
|
+
id: "handcash",
|
|
770
|
+
name: "HandCash",
|
|
771
|
+
icon: o.icon,
|
|
772
|
+
kind: "handcash",
|
|
773
|
+
isBwalletX: false,
|
|
774
|
+
substrate: "adapter",
|
|
775
|
+
async connect() {
|
|
776
|
+
const p = await o.login();
|
|
777
|
+
return {
|
|
778
|
+
wallet: null,
|
|
779
|
+
payer: o.pay ? { pay: o.pay } : null,
|
|
780
|
+
identityKey: p.identityKey ?? null,
|
|
781
|
+
handle: p.handle,
|
|
782
|
+
paymail: p.paymail ?? `${p.handle.replace(/^\$/, "")}@handcash.io`,
|
|
783
|
+
optionId: opt.id,
|
|
784
|
+
kind: "handcash",
|
|
785
|
+
transitional: true
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
return opt;
|
|
790
|
+
}
|
|
791
|
+
var NAMES = { [RDNS_EXTENSION]: "bWalletX", "space.bwallet.mobile": "bWalletX" };
|
|
792
|
+
async function probe(substrate, ms) {
|
|
793
|
+
try {
|
|
794
|
+
const w = new import_sdk4.WalletClient(substrate);
|
|
795
|
+
const ok = await Promise.race([w.isAuthenticated({}).then(() => true), new Promise((r) => setTimeout(() => r(false), ms))]);
|
|
796
|
+
return ok ? w : null;
|
|
797
|
+
} catch {
|
|
798
|
+
return null;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function cwiIsAnnounced(cwi, announced) {
|
|
802
|
+
return announced.some((o) => o.wallet === cwi || o.rdns === RDNS_MOBILE);
|
|
803
|
+
}
|
|
804
|
+
async function discoverWallets(o = {}) {
|
|
805
|
+
const out = [];
|
|
806
|
+
const ms = o.ms ?? 300;
|
|
807
|
+
if (typeof window !== "undefined") {
|
|
808
|
+
const seen = /* @__PURE__ */ new Map();
|
|
809
|
+
const onAnnounce = (e) => {
|
|
810
|
+
const d = e.detail;
|
|
811
|
+
if (!d?.wallet) return;
|
|
812
|
+
const rdns = d.info?.rdns ?? d.info?.uuid ?? `wallet-${seen.size}`;
|
|
813
|
+
if (seen.has(rdns)) return;
|
|
814
|
+
const isB = BWALLETX_RDNS.includes(rdns);
|
|
815
|
+
const substrate = rdns === RDNS_EXTENSION ? "extension" : rdns === RDNS_MOBILE ? "in-app" : "injected";
|
|
816
|
+
seen.set(rdns, brc100Option(d.wallet, { id: rdns, rdns, name: NAMES[rdns] ?? d.info?.name ?? rdns, icon: d.info?.icon, isBwalletX: isB, substrate }));
|
|
817
|
+
};
|
|
818
|
+
window.addEventListener("brc100:announceWallet", onAnnounce);
|
|
819
|
+
window.dispatchEvent(new Event("brc100:requestWallet"));
|
|
820
|
+
const local = o.probeLocal ? Promise.all([probe("json-api", ms), probe("Cicada", ms)]) : Promise.resolve([null, null]);
|
|
821
|
+
await new Promise((r) => setTimeout(r, seen.size > 0 || window.CWI ? Math.min(ms, 50) : ms));
|
|
822
|
+
window.removeEventListener("brc100:announceWallet", onAnnounce);
|
|
823
|
+
out.push(...seen.values());
|
|
824
|
+
if (![...seen.values()].some((w) => w.isBwalletX) && isFramed()) {
|
|
825
|
+
const x = await probeXdm(Math.max(ms, 1200));
|
|
826
|
+
if (x) out.push(brc100Option(x, { id: "bwalletx-xdm", name: "bWalletX", isBwalletX: true, substrate: "xdm" }));
|
|
827
|
+
}
|
|
828
|
+
const cwi = window.CWI;
|
|
829
|
+
if (cwi && !cwiIsAnnounced(cwi, [...seen.values()])) {
|
|
830
|
+
out.push(brc100Option(cwi, { id: "window.CWI", name: seen.size ? "Another browser wallet" : "Browser wallet", isBwalletX: false, substrate: "window.CWI" }));
|
|
831
|
+
}
|
|
832
|
+
const [json, cicada] = await local;
|
|
833
|
+
if (json) out.push(brc100Option(json, { id: "localhost:3321", name: "Desktop wallet (localhost:3321)", substrate: "json-api" }));
|
|
834
|
+
if (cicada) out.push(brc100Option(cicada, { id: "localhost:3301", name: "Desktop wallet (localhost:3301)", substrate: "cicada" }));
|
|
835
|
+
}
|
|
836
|
+
if (o.pairing) out.push(pairingOption(o.pairing));
|
|
837
|
+
out.push(...o.extra ?? []);
|
|
838
|
+
const rank = (w) => w.isBwalletX ? 0 : w.kind === "handcash" ? 2 : 1;
|
|
839
|
+
return out.map((w, i) => ({ w, i })).sort((a, b) => rank(a.w) - rank(b.w) || a.i - b.i).map((x) => x.w);
|
|
840
|
+
}
|
|
841
|
+
var DESKTOP_OPTION_ID = "desktop";
|
|
842
|
+
function desktopOption(ms = 1500) {
|
|
843
|
+
const opt = {
|
|
844
|
+
id: DESKTOP_OPTION_ID,
|
|
845
|
+
name: "Desktop wallet",
|
|
846
|
+
kind: "brc100",
|
|
847
|
+
isBwalletX: false,
|
|
848
|
+
substrate: "json-api",
|
|
849
|
+
async connect(c) {
|
|
850
|
+
const [json, cicada] = await Promise.all([probe("json-api", ms), probe("Cicada", ms)]);
|
|
851
|
+
const w = json ?? cicada;
|
|
852
|
+
if (!w) throw Object.assign(new Error("No desktop wallet answered on this computer"), { code: "NOT_FOUND" });
|
|
853
|
+
opt.wallet = w;
|
|
854
|
+
opt.substrate = json ? "json-api" : "cicada";
|
|
855
|
+
return brc100Connected(w, opt, c);
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
return opt;
|
|
859
|
+
}
|
|
860
|
+
function autoChoice(list, force = false) {
|
|
861
|
+
const detected = list.filter((w) => w.kind === "brc100");
|
|
862
|
+
const others = list.filter((w) => w.kind === "handcash" || w.kind === "custom");
|
|
863
|
+
if (force) return detected[0] ?? list.find((w) => w.kind !== "pairing") ?? list[0] ?? null;
|
|
864
|
+
if (!list.length) return null;
|
|
865
|
+
if (list.length === 1) return list[0];
|
|
866
|
+
if (detected.length === 1 && others.length === 0) return detected[0];
|
|
867
|
+
if (detected.length === 0 && others.length === 0) return list[0];
|
|
868
|
+
return void 0;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// src/choice.ts
|
|
872
|
+
var BWALLETX_TYPE = "bwalletx";
|
|
873
|
+
function substrateOf(o) {
|
|
874
|
+
if (o.substrate) return o.substrate;
|
|
875
|
+
if (o.kind === "pairing") return "pairing";
|
|
876
|
+
if (o.isBwalletX && o.kind === "brc100") return o.rdns === RDNS_MOBILE ? "in-app" : o.id === "bwalletx-xdm" ? "xdm" : "extension";
|
|
877
|
+
if (o.kind === "handcash" || o.kind === "custom") return "adapter";
|
|
878
|
+
if (o.rdns === RDNS_EXTENSION) return "extension";
|
|
879
|
+
if (o.rdns === RDNS_MOBILE) return "in-app";
|
|
880
|
+
if (o.id === "window.CWI") return "window.CWI";
|
|
881
|
+
if (o.id === "localhost:3321" || o.id === DESKTOP_OPTION_ID) return "json-api";
|
|
882
|
+
if (o.id === "localhost:3301") return "cicada";
|
|
883
|
+
return "injected";
|
|
884
|
+
}
|
|
885
|
+
function walletTypeOf(o) {
|
|
886
|
+
return o.isBwalletX ? BWALLETX_TYPE : o.rdns ?? o.id;
|
|
887
|
+
}
|
|
888
|
+
var OWN_MARKERS = ["extension", "in-app", "xdm"];
|
|
889
|
+
function isPresentBwalletX(o) {
|
|
890
|
+
return o.isBwalletX && o.kind === "brc100" && !!o.wallet && OWN_MARKERS.includes(substrateOf(o));
|
|
891
|
+
}
|
|
892
|
+
function hasBwalletX(list) {
|
|
893
|
+
return list.some(isPresentBwalletX);
|
|
894
|
+
}
|
|
895
|
+
function otherDetected(list) {
|
|
896
|
+
return list.filter((o) => !o.isBwalletX && o.kind === "brc100" && !!o.wallet);
|
|
897
|
+
}
|
|
898
|
+
function chooserOrder(list) {
|
|
899
|
+
const rank = (o) => isPresentBwalletX(o) ? 0 : o.isBwalletX ? 1 : o.kind === "brc100" ? 2 : o.kind === "custom" ? 3 : 4;
|
|
900
|
+
return list.map((o, i) => ({ o, i })).sort((a, b) => rank(a.o) - rank(b.o) || a.i - b.i).map((x) => x.o);
|
|
901
|
+
}
|
|
902
|
+
function decideWallet(list, o = {}) {
|
|
903
|
+
if (o.walletId) {
|
|
904
|
+
const w = list.find((x) => x.id === o.walletId);
|
|
905
|
+
if (w) return { option: w, reason: "requested" };
|
|
906
|
+
}
|
|
907
|
+
if (o.chooser) return { option: null, reason: list.length || o.picker !== false ? "chooser" : "none" };
|
|
908
|
+
if (o.fastPath === false) {
|
|
909
|
+
const a = autoChoice([...list], o.picker === false);
|
|
910
|
+
return a === void 0 ? { option: null, reason: "chooser" } : a === null ? { option: null, reason: "none" } : { option: a, reason: "legacy" };
|
|
911
|
+
}
|
|
912
|
+
const b = list.find(isPresentBwalletX);
|
|
913
|
+
if (b) return { option: b, reason: "bwalletx" };
|
|
914
|
+
if (o.picker === false) {
|
|
915
|
+
const phone = list.find((x) => x.isBwalletX);
|
|
916
|
+
return phone ? { option: phone, reason: "bwalletx-phone" } : { option: null, reason: "none" };
|
|
917
|
+
}
|
|
918
|
+
return list.length ? { option: null, reason: "chooser" } : { option: null, reason: "none" };
|
|
919
|
+
}
|
|
920
|
+
function outcomeOf(e) {
|
|
921
|
+
const err = e;
|
|
922
|
+
if (err?.code === "NOT_FOUND") return "not-found";
|
|
923
|
+
if (err?.name === "AbortError" || err?.message === "cancelled") return "cancelled";
|
|
924
|
+
if (isUserRejection(e)) return "rejected";
|
|
925
|
+
return "error";
|
|
926
|
+
}
|
|
927
|
+
function attemptFor(option, outcome, startedAt, now = Date.now()) {
|
|
928
|
+
return {
|
|
929
|
+
walletType: option ? walletTypeOf(option) : null,
|
|
930
|
+
substrate: option ? substrateOf(option) : null,
|
|
931
|
+
outcome,
|
|
932
|
+
ms: Math.max(0, Math.round(now - startedAt))
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
function reportAttempt(hook, a) {
|
|
936
|
+
if (!hook) return;
|
|
937
|
+
try {
|
|
938
|
+
hook(Object.freeze({ walletType: a.walletType, substrate: a.substrate, outcome: a.outcome, ms: a.ms }));
|
|
939
|
+
} catch {
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// src/connect-flow.ts
|
|
944
|
+
async function runConnect(o) {
|
|
945
|
+
const started = Date.now();
|
|
946
|
+
const { list } = o;
|
|
947
|
+
let choice = null;
|
|
948
|
+
let reason = "none";
|
|
949
|
+
try {
|
|
950
|
+
const d = decideWallet(list, { walletId: o.walletId, chooser: o.chooser, picker: o.picker, fastPath: o.fastPath });
|
|
951
|
+
choice = d.option;
|
|
952
|
+
reason = d.reason;
|
|
953
|
+
if (!choice && d.reason === "chooser") choice = await o.pick(list);
|
|
954
|
+
if (!choice) {
|
|
955
|
+
if (d.reason === "none" || !list.length) throw Object.assign(new Error("No wallet found. Get bWalletX at bwalletx.com."), { code: "NOT_FOUND" });
|
|
956
|
+
reportAttempt(o.onAttempt, attemptFor(null, "cancelled", started));
|
|
957
|
+
return null;
|
|
958
|
+
}
|
|
959
|
+
const signIn = !!(o.challengeUrl && o.verifyUrl) && choice.kind !== "handcash";
|
|
960
|
+
const cw = await choice.connect(signIn ? { identity: false } : void 0);
|
|
961
|
+
let handle = cw.handle ?? null;
|
|
962
|
+
let paymail = cw.paymail ?? null;
|
|
963
|
+
let identityKey = cw.identityKey;
|
|
964
|
+
let signedIn = false;
|
|
965
|
+
if (cw.wallet && signIn) {
|
|
966
|
+
const { result, verified } = await signInAndVerify({
|
|
967
|
+
wallet: cw.wallet,
|
|
968
|
+
identityKey: cw.identityKey ?? void 0,
|
|
969
|
+
// The key that signed in here last time: a repeat sign-in is then the signature alone.
|
|
970
|
+
identityKeyHint: rememberedIdentity(),
|
|
971
|
+
challengeNeedsIdentityKey: o.challengeNeedsIdentityKey,
|
|
972
|
+
challenge: (key) => o.post(o.challengeUrl, key ? { identityKey: key } : {}),
|
|
973
|
+
verify: (r) => o.post(o.verifyUrl, { identityKey: r.identityKey, nonce: r.nonce, signature: r.signature })
|
|
974
|
+
});
|
|
975
|
+
rememberIdentity(result.identityKey);
|
|
976
|
+
identityKey = result.identityKey;
|
|
977
|
+
handle = verified.handle ?? handle;
|
|
978
|
+
paymail = verified.paymail ?? paymail;
|
|
979
|
+
signedIn = true;
|
|
980
|
+
} else if (cw.wallet && !identityKey) {
|
|
981
|
+
identityKey = (await cw.wallet.getPublicKey({ identityKey: true })).publicKey;
|
|
982
|
+
}
|
|
983
|
+
reportAttempt(o.onAttempt, attemptFor(choice, signedIn ? "signed-in" : "connected", started));
|
|
984
|
+
return { connected: cw, identityKey, handle, paymail, signedIn, walletType: walletTypeOf(choice), substrate: substrateOf(choice), reason };
|
|
985
|
+
} catch (e) {
|
|
986
|
+
reportAttempt(o.onAttempt, attemptFor(choice, outcomeOf(e), started));
|
|
987
|
+
throw e;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
623
991
|
// src/modal.ts
|
|
624
992
|
var import_qrcode_generator = __toESM(require("qrcode-generator"), 1);
|
|
625
993
|
|
|
626
994
|
// src/access-client.ts
|
|
627
|
-
var
|
|
995
|
+
var import_sdk5 = require("@bsv/sdk");
|
|
628
996
|
|
|
629
997
|
// src/access.ts
|
|
630
998
|
var ACCESS_PROTOCOL = [2, "bwalletx access"];
|
|
@@ -762,7 +1130,7 @@ async function proveHoldings(wallet, challenge, tokenId, basket = "bsv21") {
|
|
|
762
1130
|
const counterparty = ci.counterparty ?? "self";
|
|
763
1131
|
for (const protocolID of ci.protocolID ? [ci.protocolID] : [ONESAT, LEGACY_ONESAT]) {
|
|
764
1132
|
const { publicKey } = await wallet.getPublicKey({ protocolID, keyID: ci.keyID, counterparty });
|
|
765
|
-
const h =
|
|
1133
|
+
const h = import_sdk5.Utils.toHex(import_sdk5.Hash.hash160(import_sdk5.PublicKey.fromString(publicKey).encode(true)));
|
|
766
1134
|
if (want && h !== want) continue;
|
|
767
1135
|
const existing = byKey.get(publicKey);
|
|
768
1136
|
if (existing) {
|
|
@@ -812,7 +1180,7 @@ async function requestAccess(o) {
|
|
|
812
1180
|
var SIGNIN_TAG = "bwalletx-signin";
|
|
813
1181
|
var GET_URL = "https://bwalletx.com/get";
|
|
814
1182
|
var WEB_WALLET_URL = "https://web.bwalletx.com";
|
|
815
|
-
var VERBS = { signin: "Sign in with", continue: "Continue with", connect: "Connect" };
|
|
1183
|
+
var VERBS = { signin: "Sign in with", continue: "Continue with", connect: "Connect", get: "Get", play: "Play with" };
|
|
816
1184
|
function signinText(label = "signin", brand = "bWalletX") {
|
|
817
1185
|
return `${VERBS[label] ?? VERBS.signin} ${brand}`;
|
|
818
1186
|
}
|
|
@@ -867,7 +1235,50 @@ button[aria-busy=true]{cursor:wait}
|
|
|
867
1235
|
.nf a:focus-visible{outline:2px solid #b45309;outline-offset:2px;border-radius:2px}
|
|
868
1236
|
:host([theme=dark]) .nf,:host-context(.dark) .nf{color:#a1a1aa}
|
|
869
1237
|
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
1238
|
+
/* pending: one plain line under the button */
|
|
1239
|
+
.note{margin:8px 0 0;width:0;min-width:100%;font-size:12px;line-height:1.4;color:#71717a;text-align:center;overflow-wrap:break-word}
|
|
1240
|
+
:host([theme=dark]) .note,:host-context(.dark) .note{color:#a1a1aa}
|
|
1241
|
+
/* "Other wallets" (0.5.6): one small link under the button */
|
|
1242
|
+
.ow{margin:8px 0 0;width:0;min-width:100%;font-size:12px;line-height:1.4;color:#71717a;text-align:center}
|
|
1243
|
+
:host([theme=dark]) .ow,:host-context(.dark) .ow{color:#a1a1aa}
|
|
1244
|
+
.lnk{all:unset;cursor:pointer;color:inherit;font-weight:600;text-decoration:underline;text-underline-offset:2px}
|
|
1245
|
+
.lnk:focus-visible{outline:2px solid #b45309;outline-offset:2px;border-radius:2px}
|
|
1246
|
+
/* error: one line plus Retry */
|
|
1247
|
+
.err{margin:0 0 8px;width:0;min-width:100%;font-size:13px;font-weight:600;line-height:1.4;color:#b91c1c;text-align:center;overflow-wrap:anywhere}
|
|
1248
|
+
:host([theme=dark]) .err,:host-context(.dark) .err{color:#fca5a5}
|
|
1249
|
+
/* signed in: the badge */
|
|
1250
|
+
button.chip{display:inline-flex;align-items:center;justify-content:flex-start;gap:10px;width:auto;max-width:100%;padding:5px 14px 5px 5px;border-radius:999px;
|
|
1251
|
+
background:#0b0b0c;color:#ffd24d;border:1px solid rgba(255,210,77,.55);box-shadow:none;font-size:15px}
|
|
1252
|
+
button.chip:hover{background:#151517;border-color:#ffd24d}
|
|
1253
|
+
button.chip:active{background:#000;transform:none}
|
|
1254
|
+
button.chip:focus-visible{outline:2px solid #ffd24d;outline-offset:3px}
|
|
1255
|
+
:host([theme=light]) button.chip{background:#fff;color:#09090b;border-color:#f59e0b}
|
|
1256
|
+
:host([theme=light]) button.chip:hover{background:#fffbeb}
|
|
1257
|
+
:host([full-width]) button.chip{width:100%}
|
|
1258
|
+
.av{position:relative;flex:none;display:inline-grid;place-items:center;width:30px;height:30px;border-radius:50%;overflow:hidden;
|
|
1259
|
+
background:radial-gradient(circle at 30% 30%,#ffe27a,#f5b800 55%,#b88600);color:#000;font-weight:800;font-size:14px}
|
|
1260
|
+
.av img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
|
|
1261
|
+
.name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:min(52vw,260px)}
|
|
1262
|
+
.dot{flex:none;width:8px;height:8px;border-radius:50%;background:#ffd24d;box-shadow:0 0 0 3px rgba(255,210,77,.22)}
|
|
1263
|
+
:host([size=sm]) button.chip{font-size:13px;padding:3px 12px 3px 3px;gap:8px}
|
|
1264
|
+
:host([size=sm]) .av{width:26px;height:26px}
|
|
1265
|
+
:host([size=icon]) button.chip{width:48px;height:48px;padding:0;justify-content:center}
|
|
1266
|
+
:host([size=icon]) .name,:host([size=icon]) .dot{display:none}
|
|
1267
|
+
:host([size=icon]) .av{width:40px;height:40px}
|
|
870
1268
|
`;
|
|
1269
|
+
var MENU_CSS = `
|
|
1270
|
+
:host{all:initial}
|
|
1271
|
+
.menu{position:fixed;z-index:2147483600;box-sizing:border-box;min-width:200px;padding:6px;border-radius:14px;background:#0b0b0c;color:#ffd24d;
|
|
1272
|
+
border:1px solid rgba(255,210,77,.55);box-shadow:0 12px 32px rgba(0,0,0,.45);font:600 14px/1.3 Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
|
|
1273
|
+
.who{padding:8px 10px 10px;font-size:12px;font-weight:500;color:#e7d9a8;overflow-wrap:anywhere;border-bottom:1px solid rgba(255,210,77,.3);margin-bottom:4px}
|
|
1274
|
+
.item{all:unset;box-sizing:border-box;display:block;width:100%;padding:11px 10px;border-radius:10px;cursor:pointer;color:#ffd24d}
|
|
1275
|
+
.item:hover,.item:focus-visible{background:#ffd24d;color:#000;outline:none}
|
|
1276
|
+
.tip{position:fixed;z-index:2147483600;box-sizing:border-box;padding:8px 12px;border-radius:10px;background:#0b0b0c;color:#e7d9a8;
|
|
1277
|
+
border:1px solid rgba(255,210,77,.4);box-shadow:0 8px 24px rgba(0,0,0,.35);font:500 12px/1.4 Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
|
|
1278
|
+
`;
|
|
1279
|
+
var PENDING_NOTE = "Sign in: shares only your public identity key, moves no money";
|
|
1280
|
+
var PENDING_LABEL = "Approve in bWalletX\u2026";
|
|
1281
|
+
var PENDING_LABEL_OTHER = "Approve in your wallet\u2026";
|
|
871
1282
|
function b64url2(bytes) {
|
|
872
1283
|
let s = "";
|
|
873
1284
|
for (const b of bytes) s += String.fromCharCode(b);
|
|
@@ -900,14 +1311,15 @@ async function buildAuthorizeUrl(p) {
|
|
|
900
1311
|
async function postJson(url, body) {
|
|
901
1312
|
const res = await fetch(url, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
902
1313
|
const data = await res.json().catch(() => ({}));
|
|
903
|
-
if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
|
|
1314
|
+
if (!res.ok) throw Object.assign(new Error(data.error ?? `HTTP ${res.status}`), { status: res.status });
|
|
904
1315
|
return data;
|
|
905
1316
|
}
|
|
906
1317
|
async function postAccess(url, body) {
|
|
907
1318
|
const res = await fetch(url, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
908
1319
|
return res.json().catch(() => ({ error: "bad_request", message: `HTTP ${res.status}` }));
|
|
909
1320
|
}
|
|
910
|
-
var OBSERVED = ["size", "theme", "label", "brand", "subtitle", "loading", "disabled", "full-width", "aria-label", "get-url", "web-url"];
|
|
1321
|
+
var OBSERVED = ["size", "theme", "label", "brand", "subtitle", "loading", "disabled", "full-width", "aria-label", "get-url", "web-url", "state", "identity-key", "paymail", "handle", "avatar-url", "error", "other-wallets", "challenge-url"];
|
|
1322
|
+
var STATES = ["signed-out", "restoring", "pending", "signed-in", "error"];
|
|
911
1323
|
function createClass() {
|
|
912
1324
|
return class BwalletxSigninElement extends HTMLElement {
|
|
913
1325
|
static get observedAttributes() {
|
|
@@ -916,12 +1328,23 @@ function createClass() {
|
|
|
916
1328
|
#root;
|
|
917
1329
|
#notFound = false;
|
|
918
1330
|
#busy = false;
|
|
1331
|
+
#phase = "signed-out";
|
|
1332
|
+
#account = null;
|
|
1333
|
+
#error = "";
|
|
1334
|
+
#restored = false;
|
|
1335
|
+
#pendingLabel = PENDING_LABEL;
|
|
1336
|
+
#menu = null;
|
|
919
1337
|
constructor() {
|
|
920
1338
|
super();
|
|
921
1339
|
this.#root = this.attachShadow({ mode: "open" });
|
|
922
1340
|
}
|
|
923
1341
|
connectedCallback() {
|
|
924
1342
|
this.#render();
|
|
1343
|
+
void this.#restore();
|
|
1344
|
+
}
|
|
1345
|
+
disconnectedCallback() {
|
|
1346
|
+
this.#menu?.close(false);
|
|
1347
|
+
this.#hideTip();
|
|
925
1348
|
}
|
|
926
1349
|
attributeChangedCallback() {
|
|
927
1350
|
if (this.isConnected) this.#render();
|
|
@@ -933,93 +1356,402 @@ function createClass() {
|
|
|
933
1356
|
get notFound() {
|
|
934
1357
|
return this.#notFound;
|
|
935
1358
|
}
|
|
1359
|
+
/** The state shown now (the `state` attribute when set, else the element's own). */
|
|
1360
|
+
get state() {
|
|
1361
|
+
const a = this.#attr("state");
|
|
1362
|
+
return a && STATES.includes(a) ? a : this.#phase;
|
|
1363
|
+
}
|
|
1364
|
+
/** The signed-in account, or null. */
|
|
1365
|
+
get account() {
|
|
1366
|
+
if (!this.#controlled) return this.#account;
|
|
1367
|
+
const identityKey = this.#attr("identity-key");
|
|
1368
|
+
return identityKey ? { identityKey, paymail: this.#attr("paymail"), handle: this.#attr("handle"), avatarUrl: this.#attr("avatar-url") } : null;
|
|
1369
|
+
}
|
|
1370
|
+
/** True while the menu is open (tests). */
|
|
1371
|
+
get menuOpen() {
|
|
1372
|
+
return !!this.#menu;
|
|
1373
|
+
}
|
|
936
1374
|
focus(opts) {
|
|
937
1375
|
this.button?.focus(opts);
|
|
938
1376
|
}
|
|
1377
|
+
get #controlled() {
|
|
1378
|
+
return this.hasAttribute("state");
|
|
1379
|
+
}
|
|
1380
|
+
get #stateful() {
|
|
1381
|
+
return !this.hasAttribute("stateless");
|
|
1382
|
+
}
|
|
939
1383
|
#attr(name) {
|
|
940
1384
|
return this.getAttribute(name);
|
|
941
1385
|
}
|
|
1386
|
+
/** Restore the badge from session-url once, with no wallet call. */
|
|
1387
|
+
async #restore() {
|
|
1388
|
+
const url = this.#attr("session-url");
|
|
1389
|
+
if (this.#restored || !url || this.#controlled || !this.#stateful) return;
|
|
1390
|
+
this.#restored = true;
|
|
1391
|
+
this.#set("restoring");
|
|
1392
|
+
try {
|
|
1393
|
+
const r = await fetch(url, { credentials: "include", headers: { accept: "application/json" } });
|
|
1394
|
+
const j = await r.json().catch(() => ({}));
|
|
1395
|
+
if (this.#phase !== "restoring") return;
|
|
1396
|
+
if (j.identityKey) this.#signedIn({ identityKey: j.identityKey, handle: j.handle ?? null, paymail: j.paymail ?? null });
|
|
1397
|
+
else this.#set("signed-out");
|
|
1398
|
+
} catch {
|
|
1399
|
+
if (this.#phase === "restoring") this.#set("signed-out");
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
#set(p) {
|
|
1403
|
+
this.#phase = p;
|
|
1404
|
+
if (p !== "signed-in") this.#menu?.close(false);
|
|
1405
|
+
this.#render();
|
|
1406
|
+
}
|
|
1407
|
+
#signedIn(a) {
|
|
1408
|
+
this.#account = a;
|
|
1409
|
+
this.#error = "";
|
|
1410
|
+
this.#set("signed-in");
|
|
1411
|
+
const lookup = this.#attr("lookup-url");
|
|
1412
|
+
if (!a.paymail && lookup !== "off") {
|
|
1413
|
+
void lookupPaymail(a.identityKey, lookup ?? void 0).then((pm) => {
|
|
1414
|
+
if (pm && this.#account?.identityKey === a.identityKey && !this.#account.paymail) {
|
|
1415
|
+
this.#account = { ...this.#account, paymail: pm };
|
|
1416
|
+
this.#render();
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
942
1421
|
#render() {
|
|
943
1422
|
const size = this.#attr("size") ?? "lg";
|
|
944
1423
|
const brand = this.#attr("brand") === "bWallet" ? "bWallet" : "bWalletX";
|
|
945
|
-
const
|
|
1424
|
+
const esc = (s) => s.replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
1425
|
+
const state = this.state;
|
|
1426
|
+
const acct = this.account;
|
|
1427
|
+
if (state === "signed-in" && acct) {
|
|
1428
|
+
this.#hideTip();
|
|
1429
|
+
const name = accountLabel(acct);
|
|
1430
|
+
const avatar = acct.avatarUrl ?? avatarUrlFor(acct.identityKey);
|
|
1431
|
+
const initial = (name.replace(/^\$/, "").charAt(0) || "b").toUpperCase();
|
|
1432
|
+
this.#root.innerHTML = `<style>${SIGNIN_CSS}</style><button type="button" class="chip" part="button chip" aria-haspopup="menu" aria-expanded="${this.#menu ? "true" : "false"}" aria-label="Signed in with bWalletX as ${esc(name)}. Account menu" title="Signed in as ${esc(name)}"><span class="av" part="avatar" aria-hidden="true">${esc(initial)}${avatar ? `<img alt="" src="${esc(avatar)}" referrerpolicy="no-referrer">` : ""}</span><span class="name" part="label">${esc(name)}</span><span class="dot" part="dot" aria-hidden="true"></span></button>`;
|
|
1433
|
+
const img = this.#root.querySelector("img");
|
|
1434
|
+
if (img) img.addEventListener("error", () => img.remove(), { once: true });
|
|
1435
|
+
const chip = this.button;
|
|
1436
|
+
chip.addEventListener("click", () => this.#menu ? this.#menu.close(true) : this.#openMenu());
|
|
1437
|
+
chip.addEventListener("keydown", (e) => {
|
|
1438
|
+
if ((e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") && !this.#menu) {
|
|
1439
|
+
e.preventDefault();
|
|
1440
|
+
this.#openMenu();
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const pending = state === "pending";
|
|
1446
|
+
const restoring = state === "restoring";
|
|
1447
|
+
const error = state === "error";
|
|
1448
|
+
const pendingLabel = this.#controlled ? PENDING_LABEL : this.#pendingLabel;
|
|
1449
|
+
const text = pending ? pendingLabel : error ? "Retry" : signinText(this.#attr("label") ?? "signin", brand);
|
|
946
1450
|
const subAttr = this.#attr("subtitle");
|
|
947
1451
|
const subDefault = brand === "bWalletX" && (size === "lg" || size === "md") ? "also works with bWallet" : null;
|
|
948
|
-
const subtitle = subAttr === null || subAttr === "" || subAttr === "on" ? subAttr === "on" ? "also works with bWallet" : subDefault : subAttr === "off" ? null : subAttr;
|
|
949
|
-
const loading = this.hasAttribute("loading") || this.#busy;
|
|
1452
|
+
const subtitle = pending || error ? null : subAttr === null || subAttr === "" || subAttr === "on" ? subAttr === "on" ? "also works with bWallet" : subDefault : subAttr === "off" ? null : subAttr;
|
|
1453
|
+
const loading = this.hasAttribute("loading") || this.#busy || pending || restoring;
|
|
950
1454
|
const disabled = this.hasAttribute("disabled");
|
|
951
1455
|
const markPx = size === "icon" ? 24 : size === "sm" ? 15 : size === "md" ? 17 : 20;
|
|
952
|
-
const aria = this.#attr("aria-label") ?? (subtitle && size !== "icon" ? `${text}, ${subtitle}` : text);
|
|
1456
|
+
const aria = pending ? pendingLabel : this.#attr("aria-label") ?? (subtitle && size !== "icon" ? `${text}, ${subtitle}` : text);
|
|
953
1457
|
const getUrl = this.#attr("get-url") ?? GET_URL;
|
|
954
1458
|
const webUrl = this.#attr("web-url") ?? WEB_WALLET_URL;
|
|
955
|
-
const
|
|
1459
|
+
const errText = error ? (this.#controlled ? this.#attr("error") : this.#error) || "Sign-in failed" : "";
|
|
956
1460
|
const label = size === "icon" ? "" : `<span class="txt" part="text"><b part="label">${esc(text)}</b>${subtitle ? `<small part="subtitle">${esc(subtitle)}</small>` : ""}</span>`;
|
|
957
1461
|
const mark = loading ? '<span class="spin" aria-hidden="true"></span>' : markSvg(markPx);
|
|
958
|
-
const
|
|
959
|
-
|
|
1462
|
+
const otherOff = this.#attr("other-wallets") === "off";
|
|
1463
|
+
const otherLink = '<button type="button" class="lnk" data-act="other" part="other-wallets-link">Other wallets</button>';
|
|
1464
|
+
const nf = this.#notFound ? `<p class="nf" part="not-found" role="status">bWalletX wasn't found in this browser. <a href="${esc(getUrl)}" target="_blank" rel="noopener">Get ${brand}</a> or <a href="${esc(webUrl)}" target="_blank" rel="noopener">use the web wallet</a>.${otherOff ? "" : ` ${otherLink}`}</p>` : "";
|
|
1465
|
+
const ow = !this.#notFound && !pending && !restoring && this.#otherWalletsShown(size) ? `<p class="ow" part="other-wallets">${otherLink}</p>` : "";
|
|
1466
|
+
const err = error ? `<p class="err" part="error" role="alert">${esc(errText)}</p>` : "";
|
|
1467
|
+
const compact = size === "sm" || size === "icon";
|
|
1468
|
+
const note = pending && !compact ? `<p class="note" part="note">${esc(PENDING_NOTE)}</p>` : "";
|
|
1469
|
+
const status = pending ? `<span class="sr" role="status">${esc(pendingLabel)}</span>` : loading ? '<span class="sr" role="status">Connecting to bWalletX\u2026</span>' : "";
|
|
1470
|
+
this.#root.innerHTML = `<style>${SIGNIN_CSS}</style>${err}<button type="button" part="button" aria-label="${esc(aria)}" title="${esc(subtitle ? `${text} (${subtitle})` : text)}"${disabled ? " disabled" : ""}${loading ? ' aria-busy="true"' : ""}><span class="mark" part="mark">${mark}</span>${label}</button>${status}${note}${ow}${nf}`;
|
|
960
1471
|
this.button.addEventListener("click", () => void this.#click());
|
|
1472
|
+
for (const l of Array.from(this.#root.querySelectorAll("[data-act=other]"))) l.addEventListener("click", () => void this.otherWallets());
|
|
961
1473
|
bindPrewarm(this.button);
|
|
1474
|
+
if (pending && compact) this.#showTip(PENDING_NOTE);
|
|
1475
|
+
else this.#hideTip();
|
|
1476
|
+
}
|
|
1477
|
+
#tip = null;
|
|
1478
|
+
#showTip(text) {
|
|
1479
|
+
const b = this.button;
|
|
1480
|
+
if (!b || typeof document === "undefined") return;
|
|
1481
|
+
if (!this.#tip) {
|
|
1482
|
+
const host = document.createElement("div");
|
|
1483
|
+
host.setAttribute("data-bwalletx-tip", "");
|
|
1484
|
+
const root = host.attachShadow({ mode: "open" });
|
|
1485
|
+
root.innerHTML = `<style>${MENU_CSS}</style><div class="tip" role="note"></div>`;
|
|
1486
|
+
document.body.appendChild(host);
|
|
1487
|
+
this.#tip = host;
|
|
1488
|
+
}
|
|
1489
|
+
const tip2 = this.#tip.shadowRoot.querySelector(".tip");
|
|
1490
|
+
tip2.textContent = text;
|
|
1491
|
+
const r = b.getBoundingClientRect();
|
|
1492
|
+
const vw = window.innerWidth || document.documentElement.clientWidth || 360;
|
|
1493
|
+
const width = Math.min(280, vw - 32);
|
|
1494
|
+
tip2.style.width = `${width}px`;
|
|
1495
|
+
tip2.style.left = `${Math.max(16, Math.min(r.right - width, vw - width - 16))}px`;
|
|
1496
|
+
tip2.style.top = `${r.bottom + 8}px`;
|
|
1497
|
+
}
|
|
1498
|
+
#hideTip() {
|
|
1499
|
+
this.#tip?.remove();
|
|
1500
|
+
this.#tip = null;
|
|
1501
|
+
}
|
|
1502
|
+
#openMenu() {
|
|
1503
|
+
const acct = this.account;
|
|
1504
|
+
const chip = this.button;
|
|
1505
|
+
if (!acct || !chip || typeof document === "undefined") return;
|
|
1506
|
+
const name = accountLabel(acct);
|
|
1507
|
+
const host = document.createElement("div");
|
|
1508
|
+
host.setAttribute("data-bwalletx-menu", "");
|
|
1509
|
+
const root = host.attachShadow({ mode: "open" });
|
|
1510
|
+
const style = document.createElement("style");
|
|
1511
|
+
style.textContent = MENU_CSS;
|
|
1512
|
+
const menu = document.createElement("div");
|
|
1513
|
+
menu.className = "menu";
|
|
1514
|
+
menu.setAttribute("role", "menu");
|
|
1515
|
+
menu.setAttribute("aria-label", "bWalletX account");
|
|
1516
|
+
const who = document.createElement("div");
|
|
1517
|
+
who.className = "who";
|
|
1518
|
+
who.textContent = `Signed in as ${acct.paymail || name}`;
|
|
1519
|
+
const item = (text, run) => {
|
|
1520
|
+
const b = document.createElement("button");
|
|
1521
|
+
b.type = "button";
|
|
1522
|
+
b.className = "item";
|
|
1523
|
+
b.setAttribute("role", "menuitem");
|
|
1524
|
+
b.tabIndex = -1;
|
|
1525
|
+
b.textContent = text;
|
|
1526
|
+
b.addEventListener("click", () => run(b));
|
|
1527
|
+
return b;
|
|
1528
|
+
};
|
|
1529
|
+
const copyWhat = acct.paymail ? "paymail" : "key";
|
|
1530
|
+
const copy = item(`Copy ${copyWhat}`, (b) => {
|
|
1531
|
+
void copyText(acct.paymail || acct.identityKey).then((ok) => {
|
|
1532
|
+
b.textContent = ok ? "Copied" : `Copy ${copyWhat}`;
|
|
1533
|
+
setTimeout(() => close(true), ok ? 600 : 0);
|
|
1534
|
+
});
|
|
1535
|
+
});
|
|
1536
|
+
const out = item("Disconnect", () => {
|
|
1537
|
+
close(false);
|
|
1538
|
+
void this.signOut();
|
|
1539
|
+
});
|
|
1540
|
+
menu.append(who, copy, out);
|
|
1541
|
+
root.append(style, menu);
|
|
1542
|
+
document.body.appendChild(host);
|
|
1543
|
+
const place = () => {
|
|
1544
|
+
const r = chip.getBoundingClientRect();
|
|
1545
|
+
const vw = window.innerWidth || document.documentElement.clientWidth || 360;
|
|
1546
|
+
const vh = window.innerHeight || document.documentElement.clientHeight || 640;
|
|
1547
|
+
const width = Math.min(260, vw - 32);
|
|
1548
|
+
menu.style.width = `${width}px`;
|
|
1549
|
+
const left = Math.max(16, Math.min(r.right - width, vw - width - 16));
|
|
1550
|
+
menu.style.left = `${left}px`;
|
|
1551
|
+
const h = menu.offsetHeight || 150;
|
|
1552
|
+
const below = r.bottom + 8;
|
|
1553
|
+
menu.style.top = `${below + h > vh && r.top - 8 - h > 0 ? r.top - 8 - h : below}px`;
|
|
1554
|
+
};
|
|
1555
|
+
place();
|
|
1556
|
+
const items = [copy, out];
|
|
1557
|
+
const focusAt = (i) => items[(i + items.length) % items.length].focus();
|
|
1558
|
+
const onKey = (e) => {
|
|
1559
|
+
const i = items.indexOf(root.activeElement);
|
|
1560
|
+
if (e.key === "Escape") {
|
|
1561
|
+
e.preventDefault();
|
|
1562
|
+
close(true);
|
|
1563
|
+
} else if (e.key === "ArrowDown") {
|
|
1564
|
+
e.preventDefault();
|
|
1565
|
+
focusAt(i + 1);
|
|
1566
|
+
} else if (e.key === "ArrowUp") {
|
|
1567
|
+
e.preventDefault();
|
|
1568
|
+
focusAt(i - 1);
|
|
1569
|
+
} else if (e.key === "Home") {
|
|
1570
|
+
e.preventDefault();
|
|
1571
|
+
focusAt(0);
|
|
1572
|
+
} else if (e.key === "End") {
|
|
1573
|
+
e.preventDefault();
|
|
1574
|
+
focusAt(items.length - 1);
|
|
1575
|
+
} else if (e.key === "Tab") close(false);
|
|
1576
|
+
};
|
|
1577
|
+
const onDown = (e) => {
|
|
1578
|
+
const path = e.composedPath?.() ?? [];
|
|
1579
|
+
if (!path.includes(host) && !path.includes(this)) close(false);
|
|
1580
|
+
};
|
|
1581
|
+
const onMove = () => close(false);
|
|
1582
|
+
menu.addEventListener("keydown", onKey);
|
|
1583
|
+
document.addEventListener("pointerdown", onDown, true);
|
|
1584
|
+
window.addEventListener("resize", onMove);
|
|
1585
|
+
window.addEventListener("scroll", place, true);
|
|
1586
|
+
const close = (focusChip = false) => {
|
|
1587
|
+
if (this.#menu?.host !== host) return;
|
|
1588
|
+
this.#menu = null;
|
|
1589
|
+
menu.removeEventListener("keydown", onKey);
|
|
1590
|
+
document.removeEventListener("pointerdown", onDown, true);
|
|
1591
|
+
window.removeEventListener("resize", onMove);
|
|
1592
|
+
window.removeEventListener("scroll", place, true);
|
|
1593
|
+
host.remove();
|
|
1594
|
+
this.button?.setAttribute("aria-expanded", "false");
|
|
1595
|
+
if (focusChip) this.button?.focus();
|
|
1596
|
+
};
|
|
1597
|
+
this.#menu = { host, close };
|
|
1598
|
+
chip.setAttribute("aria-expanded", "true");
|
|
1599
|
+
focusAt(0);
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Disconnect: emits bwalletx-disconnect (cancelable). Unless a listener cancels it (BwalletXProvider signs out
|
|
1603
|
+
* itself) or the element is driven by `state`, posts logout-url and shows signed out.
|
|
1604
|
+
*/
|
|
1605
|
+
async signOut() {
|
|
1606
|
+
this.#menu?.close(false);
|
|
1607
|
+
if (!this.#emit("bwalletx-disconnect", { element: this, account: this.account }, true)) return;
|
|
1608
|
+
if (this.#controlled) return;
|
|
1609
|
+
const url = this.#attr("logout-url");
|
|
1610
|
+
if (url) await fetch(url, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: "{}" }).catch(() => void 0);
|
|
1611
|
+
this.#account = null;
|
|
1612
|
+
this.#set("signed-out");
|
|
1613
|
+
this.#emit("bwalletx-signed-out");
|
|
1614
|
+
this.button?.focus();
|
|
962
1615
|
}
|
|
963
1616
|
#emit(type, detail, cancelable = false) {
|
|
964
1617
|
return this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable }));
|
|
965
1618
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
this.#
|
|
1619
|
+
/** "Other wallets" is offered under the button: other-wallets on, or auto at lg/md when this element signs in. */
|
|
1620
|
+
#otherWalletsShown(size) {
|
|
1621
|
+
const a = this.#attr("other-wallets");
|
|
1622
|
+
if (a === "off") return false;
|
|
1623
|
+
if (a === "on") return true;
|
|
1624
|
+
return (size === "lg" || size === "md") && (this.#controlled || !!this.#attr("challenge-url"));
|
|
969
1625
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1626
|
+
/** Open the wallet chooser (the "Other wallets" link): phone QR, other browser wallets, desktop wallets. */
|
|
1627
|
+
otherWallets() {
|
|
1628
|
+
return this.#click("chooser");
|
|
1629
|
+
}
|
|
1630
|
+
#attempt(option, outcome, started) {
|
|
1631
|
+
this.#emit("bwalletx-attempt", attemptFor(option, outcome, started));
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Which wallet: bWalletX by its own marker goes straight to its sheet; otherwise the chooser ("Get bWalletX" first)
|
|
1635
|
+
* when another wallet is here or the user asked; otherwise 'not-found'. null: the chooser was closed.
|
|
1636
|
+
*/
|
|
1637
|
+
async #choose(mode, ms, pairing) {
|
|
1638
|
+
if (mode !== "chooser") {
|
|
1639
|
+
const f = await discoverBwalletX(ms);
|
|
1640
|
+
if (f) {
|
|
1641
|
+
const substrate = f.rdns === RDNS_EXTENSION ? "extension" : f.rdns === RDNS_MOBILE ? "in-app" : "xdm";
|
|
1642
|
+
return brc100Option(f.wallet, { id: f.rdns ?? "bwalletx-xdm", rdns: f.rdns ?? void 0, name: "bWalletX", isBwalletX: true, substrate });
|
|
1643
|
+
}
|
|
978
1644
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1645
|
+
const list = await discoverWallets({ ms: 50, pairing: () => pairing });
|
|
1646
|
+
if (mode !== "chooser" && !hasBwalletX(list) && !otherDetected(list).length) return "not-found";
|
|
1647
|
+
return chooseWallet(list, { getUrl: this.#attr("get-url") ?? void 0 });
|
|
1648
|
+
}
|
|
1649
|
+
async #click(mode = "signin") {
|
|
1650
|
+
if (this.hasAttribute("disabled") || this.hasAttribute("loading") || this.#busy || this.state === "pending") return;
|
|
1651
|
+
if (!this.#emit("bwalletx-signin", { element: this, mode }, true)) return;
|
|
1652
|
+
if (mode === "signin") {
|
|
1653
|
+
const href = this.#attr("href");
|
|
1654
|
+
const authorizeUrl = this.#attr("authorize-url");
|
|
1655
|
+
if (authorizeUrl) {
|
|
1656
|
+
location.assign(await buildAuthorizeUrl({ authorizeUrl, clientId: this.#attr("client-id"), redirectUri: this.#attr("redirect-uri"), scope: this.#attr("scope") }));
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
if (href) {
|
|
1660
|
+
location.assign(href);
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
982
1663
|
}
|
|
983
1664
|
const challengeUrl = this.#attr("challenge-url");
|
|
984
1665
|
if (!challengeUrl) return;
|
|
1666
|
+
const own = this.#stateful && !this.#controlled;
|
|
1667
|
+
const prev = this.#phase === "signed-in" ? "signed-in" : "signed-out";
|
|
1668
|
+
const started = Date.now();
|
|
1669
|
+
const pairing = pairingModal();
|
|
1670
|
+
let option = null;
|
|
985
1671
|
this.#notFound = false;
|
|
986
|
-
this.#
|
|
1672
|
+
this.#busy = true;
|
|
1673
|
+
this.#pendingLabel = PENDING_LABEL;
|
|
1674
|
+
if (own) this.#set("pending");
|
|
1675
|
+
else this.#render();
|
|
987
1676
|
try {
|
|
988
1677
|
const ms = Number(this.#attr("discover-ms")) || DEFAULT_DISCOVER_MS;
|
|
989
|
-
const
|
|
990
|
-
if (
|
|
1678
|
+
const picked = await this.#choose(mode, ms, pairing);
|
|
1679
|
+
if (picked === "not-found") {
|
|
991
1680
|
this.#notFound = true;
|
|
1681
|
+
if (own) this.#phase = "signed-out";
|
|
1682
|
+
this.#attempt(null, "not-found", started);
|
|
992
1683
|
this.#emit("bwalletx-not-found");
|
|
993
1684
|
if (this.#attr("require-token")) this.#emit("bwalletx-access-denied", { code: "not_installed", message: "No wallet found" });
|
|
994
1685
|
return;
|
|
995
1686
|
}
|
|
1687
|
+
if (!picked) {
|
|
1688
|
+
if (own) this.#phase = prev;
|
|
1689
|
+
this.#attempt(null, "cancelled", started);
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
option = picked;
|
|
1693
|
+
if (!option.isBwalletX) {
|
|
1694
|
+
this.#pendingLabel = PENDING_LABEL_OTHER;
|
|
1695
|
+
if (own) this.#render();
|
|
1696
|
+
}
|
|
1697
|
+
const cw = await option.connect({ identity: false });
|
|
1698
|
+
const wallet = cw.wallet;
|
|
1699
|
+
if (!wallet) throw Object.assign(new Error("This wallet cannot sign in here"), { code: "NOT_SUPPORTED" });
|
|
1700
|
+
const sub = substrateOf(option);
|
|
1701
|
+
const method = sub === "extension" ? "extension" : sub === "in-app" || sub === "xdm" ? "in-app" : sub === "pairing" ? "pairing" : "wallet";
|
|
996
1702
|
const requireToken = this.#attr("require-token");
|
|
997
1703
|
if (requireToken) {
|
|
1704
|
+
if (own) this.#phase = "signed-out";
|
|
998
1705
|
const verifyUrl2 = this.#attr("verify-url");
|
|
999
1706
|
const min = this.#attr("min");
|
|
1000
1707
|
const minBps = this.#attr("min-bps");
|
|
1001
1708
|
const hint = { token: { id: requireToken, ...minBps ? { minBps: Number(minBps) } : { min: min ?? "1" } } };
|
|
1002
1709
|
try {
|
|
1003
1710
|
const a = await requestAccess({
|
|
1004
|
-
wallet
|
|
1711
|
+
wallet,
|
|
1005
1712
|
challenge: (identityKey) => postJson(challengeUrl, { identityKey, require: hint }),
|
|
1006
1713
|
verify: verifyUrl2 ? (response2) => postAccess(verifyUrl2, response2) : void 0
|
|
1007
1714
|
});
|
|
1715
|
+
this.#attempt(option, "signed-in", started);
|
|
1008
1716
|
this.#emit("bwalletx-access-granted", { identityKey: a.response.identityKey, response: a.verified });
|
|
1009
1717
|
} catch (e) {
|
|
1010
1718
|
const err = e instanceof AccessError ? e : new AccessError("bad_request", String(e));
|
|
1719
|
+
this.#attempt(option, outcomeOf(e), started);
|
|
1011
1720
|
this.#emit("bwalletx-access-denied", { code: err.code, message: err.message, have: err.have, need: err.need });
|
|
1012
1721
|
}
|
|
1013
1722
|
return;
|
|
1014
1723
|
}
|
|
1015
|
-
const r = await signInWithBwalletX({ wallet: found.wallet, challenge: (identityKey) => postJson(challengeUrl, { identityKey }) });
|
|
1016
1724
|
const verifyUrl = this.#attr("verify-url");
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1725
|
+
const { result: r, verified } = await signInAndVerify({
|
|
1726
|
+
wallet,
|
|
1727
|
+
identityKeyHint: verifyUrl ? rememberedIdentity() : void 0,
|
|
1728
|
+
challenge: (identityKey) => postJson(challengeUrl, identityKey ? { identityKey } : {}),
|
|
1729
|
+
verify: async (res) => verifyUrl ? await postJson(verifyUrl, { identityKey: res.identityKey, nonce: res.nonce, signature: res.signature }) : {}
|
|
1730
|
+
});
|
|
1731
|
+
if (verifyUrl) rememberIdentity(r.identityKey);
|
|
1732
|
+
const response = verifyUrl ? verified : null;
|
|
1733
|
+
if (own) this.#signedIn({ identityKey: r.identityKey, handle: response?.handle ?? null, paymail: response?.paymail ?? null });
|
|
1734
|
+
this.#attempt(option, "signed-in", started);
|
|
1735
|
+
this.#emit("bwalletx-signed-in", { identityKey: r.identityKey, method, nonce: r.nonce, signature: r.signature, response });
|
|
1019
1736
|
} catch (error) {
|
|
1020
|
-
|
|
1737
|
+
const outcome = outcomeOf(error);
|
|
1738
|
+
this.#attempt(option, outcome, started);
|
|
1739
|
+
const closedQr = outcome === "cancelled" && option?.kind === "pairing";
|
|
1740
|
+
if (own) {
|
|
1741
|
+
if (closedQr) this.#phase = prev;
|
|
1742
|
+
else {
|
|
1743
|
+
this.#error = signInErrorText(error);
|
|
1744
|
+
this.#phase = "error";
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
if (!closedQr) this.#emit("bwalletx-error", { error });
|
|
1021
1748
|
} finally {
|
|
1022
|
-
this.#
|
|
1749
|
+
this.#busy = false;
|
|
1750
|
+
try {
|
|
1751
|
+
pairing.close();
|
|
1752
|
+
} catch {
|
|
1753
|
+
}
|
|
1754
|
+
this.#render();
|
|
1023
1755
|
}
|
|
1024
1756
|
}
|
|
1025
1757
|
};
|
|
@@ -1051,7 +1783,21 @@ a.bwx-btn{display:block;text-align:center;text-decoration:none;box-sizing:border
|
|
|
1051
1783
|
.bwx-list{display:flex;flex-direction:column;gap:8px}
|
|
1052
1784
|
.bwx-opt{display:flex;align-items:center;gap:10px;text-align:left}
|
|
1053
1785
|
.bwx-opt img{width:24px;height:24px;border-radius:6px}
|
|
1054
|
-
.bwx-tag{margin-left:auto;font-size:11px;color:#8d866f}
|
|
1786
|
+
.bwx-tag{margin-left:auto;font-size:11px;color:#8d866f;white-space:nowrap}
|
|
1787
|
+
.bwx-opt{min-height:48px;box-sizing:border-box}
|
|
1788
|
+
a.bwx-btn.bwx-opt{display:flex;text-align:left}
|
|
1789
|
+
.bwx-btn:focus{outline:none}
|
|
1790
|
+
.bwx-btn:focus-visible{outline:2px solid #e8c766;outline-offset:2px}
|
|
1791
|
+
.bwx-mk{display:inline-flex;align-items:center;justify-content:center;flex:none;width:28px;height:28px;border-radius:50%;background:#000}
|
|
1792
|
+
.bwx-ico{display:inline-grid;place-items:center;flex:none;width:28px;height:28px;border-radius:8px;background:#2a2518;color:#e8c766;font-weight:700;font-size:13px}
|
|
1793
|
+
.bwx-opt img{width:28px;height:28px;border-radius:8px;flex:none}
|
|
1794
|
+
.bwx-txt{display:flex;flex-direction:column;min-width:0;gap:1px}
|
|
1795
|
+
.bwx-txt b{font-weight:600}
|
|
1796
|
+
.bwx-txt small{font-size:12px;color:#8d866f;font-weight:400}
|
|
1797
|
+
.bwx-primary .bwx-txt small{color:rgba(0,0,0,.68)}
|
|
1798
|
+
.bwx-primary .bwx-tag{color:rgba(0,0,0,.6)}
|
|
1799
|
+
.bwx-sep{font-size:12px;color:#8d866f;margin:6px 2px 0}
|
|
1800
|
+
.bwx-foot{font-size:12px;color:#8d866f;margin:12px 0 0}
|
|
1055
1801
|
`;
|
|
1056
1802
|
function mount() {
|
|
1057
1803
|
const root = document.createElement("div");
|
|
@@ -1180,105 +1926,94 @@ function pickWallet(options, o = {}) {
|
|
|
1180
1926
|
ui.box.lastChild.append(cancel);
|
|
1181
1927
|
});
|
|
1182
1928
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
kind: "brc100",
|
|
1199
|
-
isBwalletX: info.isBwalletX ?? false,
|
|
1200
|
-
connect: () => brc100Connected(wallet, opt)
|
|
1201
|
-
};
|
|
1202
|
-
return opt;
|
|
1203
|
-
}
|
|
1204
|
-
function pairingOption(pairing) {
|
|
1205
|
-
const opt = {
|
|
1206
|
-
id: "bwalletx-pairing",
|
|
1207
|
-
name: "bWalletX on your phone",
|
|
1208
|
-
kind: "pairing",
|
|
1209
|
-
isBwalletX: true,
|
|
1210
|
-
async connect() {
|
|
1211
|
-
const p = typeof pairing === "function" ? pairing() : pairing;
|
|
1212
|
-
const w = await restorePairing(p) ?? await pairBwalletX(p);
|
|
1213
|
-
return brc100Connected(w, opt);
|
|
1214
|
-
}
|
|
1215
|
-
};
|
|
1216
|
-
return opt;
|
|
1217
|
-
}
|
|
1218
|
-
function handcashAdapter(o) {
|
|
1219
|
-
const opt = {
|
|
1220
|
-
id: "handcash",
|
|
1221
|
-
name: "HandCash",
|
|
1222
|
-
icon: o.icon,
|
|
1223
|
-
kind: "handcash",
|
|
1224
|
-
isBwalletX: false,
|
|
1225
|
-
async connect() {
|
|
1226
|
-
const p = await o.login();
|
|
1227
|
-
return {
|
|
1228
|
-
wallet: null,
|
|
1229
|
-
payer: o.pay ? { pay: o.pay } : null,
|
|
1230
|
-
identityKey: p.identityKey ?? null,
|
|
1231
|
-
handle: p.handle,
|
|
1232
|
-
paymail: p.paymail ?? `${p.handle.replace(/^\$/, "")}@handcash.io`,
|
|
1233
|
-
optionId: opt.id,
|
|
1234
|
-
kind: "handcash",
|
|
1235
|
-
transitional: true
|
|
1236
|
-
};
|
|
1237
|
-
}
|
|
1238
|
-
};
|
|
1239
|
-
return opt;
|
|
1240
|
-
}
|
|
1241
|
-
var NAMES = { [RDNS_EXTENSION]: "bWalletX", "space.bwallet.mobile": "bWalletX" };
|
|
1242
|
-
async function probe(substrate, ms) {
|
|
1243
|
-
try {
|
|
1244
|
-
const w = new import_sdk5.WalletClient(substrate);
|
|
1245
|
-
const ok = await Promise.race([w.isAuthenticated({}).then(() => true), new Promise((r) => setTimeout(() => r(false), ms))]);
|
|
1246
|
-
return ok ? w : null;
|
|
1247
|
-
} catch {
|
|
1248
|
-
return null;
|
|
1929
|
+
function optionRow(w, primary, sub, tag) {
|
|
1930
|
+
const b = el("button", `bwx-btn bwx-opt${primary ? " bwx-primary" : ""}`);
|
|
1931
|
+
b.type = "button";
|
|
1932
|
+
b.dataset.wallet = w.id;
|
|
1933
|
+
if (w.isBwalletX) {
|
|
1934
|
+
const mk = el("span", "bwx-mk");
|
|
1935
|
+
mk.innerHTML = markSvg(18);
|
|
1936
|
+
b.append(mk);
|
|
1937
|
+
} else if (w.icon) {
|
|
1938
|
+
const img = el("img");
|
|
1939
|
+
img.src = w.icon;
|
|
1940
|
+
img.alt = "";
|
|
1941
|
+
b.append(img);
|
|
1942
|
+
} else {
|
|
1943
|
+
b.append(el("span", "bwx-ico", (w.name.trim().charAt(0) || "W").toUpperCase()));
|
|
1249
1944
|
}
|
|
1945
|
+
const t = el("span", "bwx-txt");
|
|
1946
|
+
t.append(el("b", "", w.name));
|
|
1947
|
+
if (sub) t.append(el("small", "", sub));
|
|
1948
|
+
b.append(t);
|
|
1949
|
+
if (tag) b.append(el("span", "bwx-tag", tag));
|
|
1950
|
+
return b;
|
|
1250
1951
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
if (seen.has(rdns)) return;
|
|
1261
|
-
const isB = BWALLETX_RDNS.includes(rdns);
|
|
1262
|
-
seen.set(rdns, brc100Option(d.wallet, { id: rdns, rdns, name: NAMES[rdns] ?? d.info?.name ?? rdns, icon: d.info?.icon, isBwalletX: isB }));
|
|
1952
|
+
function chooseWallet(options, o = {}) {
|
|
1953
|
+
if (typeof document === "undefined") return Promise.resolve(null);
|
|
1954
|
+
return new Promise((resolve) => {
|
|
1955
|
+
const ui = mount();
|
|
1956
|
+
ui.root.dataset.bwxChooser = "";
|
|
1957
|
+
const done = (w) => {
|
|
1958
|
+
document.removeEventListener("keydown", onKey, true);
|
|
1959
|
+
ui.close();
|
|
1960
|
+
resolve(w);
|
|
1263
1961
|
};
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1962
|
+
const onKey = (e) => {
|
|
1963
|
+
if (e.key === "Escape") {
|
|
1964
|
+
e.preventDefault();
|
|
1965
|
+
done(null);
|
|
1966
|
+
}
|
|
1967
|
+
};
|
|
1968
|
+
document.addEventListener("keydown", onKey, true);
|
|
1969
|
+
ui.box.append(el("h2", "", o.title ?? "Choose a wallet"));
|
|
1970
|
+
const list = el("div", "bwx-list");
|
|
1971
|
+
const here = hasBwalletX(options);
|
|
1972
|
+
if (!here) {
|
|
1973
|
+
const get = el("a", "bwx-btn bwx-opt bwx-primary");
|
|
1974
|
+
get.href = o.getUrl ?? GET_URL;
|
|
1975
|
+
get.target = "_blank";
|
|
1976
|
+
get.rel = "noopener";
|
|
1977
|
+
get.dataset.wallet = "get-bwalletx";
|
|
1978
|
+
const mk = el("span", "bwx-mk");
|
|
1979
|
+
mk.innerHTML = markSvg(18);
|
|
1980
|
+
const t = el("span", "bwx-txt");
|
|
1981
|
+
t.append(el("b", "", "Get bWalletX"), el("small", "", "Free. Every feature on this site"));
|
|
1982
|
+
get.append(mk, t);
|
|
1983
|
+
list.append(get);
|
|
1273
1984
|
}
|
|
1274
|
-
const
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1985
|
+
const ordered = chooserOrder(options);
|
|
1986
|
+
const touch = isTouchDevice();
|
|
1987
|
+
let sep = false;
|
|
1988
|
+
for (const w of ordered) {
|
|
1989
|
+
if (!here && !sep) {
|
|
1990
|
+
list.append(el("p", "bwx-sep", "Or use"));
|
|
1991
|
+
sep = true;
|
|
1992
|
+
}
|
|
1993
|
+
const sub = isPresentBwalletX(w) ? "Recommended" : w.kind === "pairing" ? touch ? "Open the bWalletX app" : "Scan a QR with your phone" : void 0;
|
|
1994
|
+
const tag = w.kind === "handcash" ? "until BRC-100" : !w.isBwalletX && w.kind === "brc100" ? "Fewer features" : void 0;
|
|
1995
|
+
const row2 = optionRow(w, isPresentBwalletX(w), sub, tag);
|
|
1996
|
+
row2.onclick = () => done(w);
|
|
1997
|
+
list.append(row2);
|
|
1998
|
+
}
|
|
1999
|
+
if (o.desktop !== false && !options.some((w) => w.substrate === "json-api" || w.substrate === "cicada")) {
|
|
2000
|
+
if (!here && !sep) list.append(el("p", "bwx-sep", "Or use"));
|
|
2001
|
+
const d = desktopOption();
|
|
2002
|
+
const row2 = optionRow(d, false, "Metanet or BSV Desktop on this computer", "Fewer features");
|
|
2003
|
+
row2.onclick = () => done(d);
|
|
2004
|
+
list.append(row2);
|
|
2005
|
+
}
|
|
2006
|
+
ui.box.append(list);
|
|
2007
|
+
if (otherDetected(options).length || !here) ui.box.append(el("p", "bwx-foot", "Some features need bWalletX."));
|
|
2008
|
+
const row = el("div", "bwx-row");
|
|
2009
|
+
const cancel = el("button", "bwx-btn", "Cancel");
|
|
2010
|
+
cancel.type = "button";
|
|
2011
|
+
cancel.onclick = () => done(null);
|
|
2012
|
+
row.append(cancel);
|
|
2013
|
+
ui.box.append(row);
|
|
2014
|
+
ui.root.onclick = (e) => e.target === ui.root && done(null);
|
|
2015
|
+
list.querySelector("a,button")?.focus();
|
|
2016
|
+
});
|
|
1282
2017
|
}
|
|
1283
2018
|
|
|
1284
2019
|
// src/pay.ts
|
|
@@ -1443,19 +2178,52 @@ async function buyToken(source, req) {
|
|
|
1443
2178
|
}
|
|
1444
2179
|
|
|
1445
2180
|
// src/react.ts
|
|
2181
|
+
function signinStateFor(v) {
|
|
2182
|
+
if (v.status === "restoring") return "restoring";
|
|
2183
|
+
if (v.status === "connecting") return "pending";
|
|
2184
|
+
if (v.status === "error") return "error";
|
|
2185
|
+
if (v.status === "connected" && v.identityKey) return "signed-in";
|
|
2186
|
+
return "signed-out";
|
|
2187
|
+
}
|
|
1446
2188
|
function BwalletxSignin(props) {
|
|
1447
2189
|
const ref = (0, import_react.useRef)(null);
|
|
1448
2190
|
const cb = (0, import_react.useRef)(props);
|
|
1449
2191
|
cb.current = props;
|
|
2192
|
+
const ctx = (0, import_react.useContext)(Ctx);
|
|
2193
|
+
const ctxRef = (0, import_react.useRef)(ctx);
|
|
2194
|
+
ctxRef.current = ctx;
|
|
2195
|
+
const driven = !!ctx && props.stateful !== false;
|
|
1450
2196
|
(0, import_react.useEffect)(() => {
|
|
1451
2197
|
defineBwalletxSignin();
|
|
1452
2198
|
const el2 = ref.current;
|
|
1453
2199
|
if (!el2) return;
|
|
1454
2200
|
const on = [
|
|
1455
|
-
[
|
|
2201
|
+
[
|
|
2202
|
+
"bwalletx-signin",
|
|
2203
|
+
(e) => {
|
|
2204
|
+
cb.current.onClick?.(e);
|
|
2205
|
+
const c = ctxRef.current;
|
|
2206
|
+
if (!e.defaultPrevented && c && cb.current.stateful !== false) {
|
|
2207
|
+
e.preventDefault();
|
|
2208
|
+
const mode = e.detail?.mode;
|
|
2209
|
+
c.connect(mode === "chooser" ? { chooser: true } : {}).catch(() => void 0);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
],
|
|
1456
2213
|
["bwalletx-signed-in", (e) => cb.current.onSignedIn?.(e.detail)],
|
|
1457
2214
|
["bwalletx-not-found", () => cb.current.onNotFound?.()],
|
|
1458
|
-
["bwalletx-error", (e) => cb.current.onError?.(e.detail.error)]
|
|
2215
|
+
["bwalletx-error", (e) => cb.current.onError?.(e.detail.error)],
|
|
2216
|
+
[
|
|
2217
|
+
"bwalletx-disconnect",
|
|
2218
|
+
(e) => {
|
|
2219
|
+
const c = ctxRef.current;
|
|
2220
|
+
if (c && cb.current.stateful !== false) {
|
|
2221
|
+
e.preventDefault();
|
|
2222
|
+
void c.disconnect().then(() => cb.current.onSignedOut?.());
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
],
|
|
2226
|
+
["bwalletx-signed-out", () => cb.current.onSignedOut?.()]
|
|
1459
2227
|
];
|
|
1460
2228
|
for (const [t, f] of on) el2.addEventListener(t, f);
|
|
1461
2229
|
return () => {
|
|
@@ -1464,6 +2232,7 @@ function BwalletxSignin(props) {
|
|
|
1464
2232
|
}, []);
|
|
1465
2233
|
const p = props;
|
|
1466
2234
|
const flag = (v) => v ? "" : void 0;
|
|
2235
|
+
const state = driven ? signinStateFor(ctx) : void 0;
|
|
1467
2236
|
return (0, import_react.createElement)("bwalletx-signin", {
|
|
1468
2237
|
ref,
|
|
1469
2238
|
id: p.id,
|
|
@@ -1479,6 +2248,16 @@ function BwalletxSignin(props) {
|
|
|
1479
2248
|
disabled: flag(p.disabled),
|
|
1480
2249
|
"challenge-url": p.challengeUrl,
|
|
1481
2250
|
"verify-url": p.verifyUrl,
|
|
2251
|
+
"session-url": driven ? void 0 : p.sessionUrl,
|
|
2252
|
+
"logout-url": driven ? void 0 : p.logoutUrl,
|
|
2253
|
+
"lookup-url": p.lookupUrl,
|
|
2254
|
+
stateless: p.stateful === false ? "" : void 0,
|
|
2255
|
+
state,
|
|
2256
|
+
"identity-key": driven ? ctx.identityKey ?? void 0 : void 0,
|
|
2257
|
+
paymail: driven ? ctx.paymail ?? void 0 : void 0,
|
|
2258
|
+
handle: driven ? ctx.handle ?? void 0 : void 0,
|
|
2259
|
+
"avatar-url": driven ? avatarUrlFor(ctx.identityKey) ?? void 0 : void 0,
|
|
2260
|
+
error: driven && ctx.status === "error" ? signInErrorText(ctx.error) : void 0,
|
|
1482
2261
|
"get-url": p.getUrl,
|
|
1483
2262
|
"web-url": p.webUrl,
|
|
1484
2263
|
href: p.href,
|
|
@@ -1487,6 +2266,7 @@ function BwalletxSignin(props) {
|
|
|
1487
2266
|
"redirect-uri": p.redirectUri,
|
|
1488
2267
|
scope: p.scope,
|
|
1489
2268
|
"discover-ms": p.discoverMs,
|
|
2269
|
+
"other-wallets": p.otherWallets,
|
|
1490
2270
|
"aria-label": p["aria-label"]
|
|
1491
2271
|
});
|
|
1492
2272
|
}
|
|
@@ -1498,6 +2278,7 @@ var empty = {
|
|
|
1498
2278
|
paymail: null,
|
|
1499
2279
|
wallet: null,
|
|
1500
2280
|
walletId: null,
|
|
2281
|
+
walletType: null,
|
|
1501
2282
|
transitional: false,
|
|
1502
2283
|
signedIn: false,
|
|
1503
2284
|
error: null
|
|
@@ -1559,45 +2340,42 @@ function BwalletXProvider(p) {
|
|
|
1559
2340
|
setState((s) => ({ ...s, status: "connecting", error: null }));
|
|
1560
2341
|
try {
|
|
1561
2342
|
const list = await listWallets();
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
2343
|
+
const fast = c.fastPath !== false;
|
|
2344
|
+
const r = await runConnect({
|
|
2345
|
+
list,
|
|
2346
|
+
walletId: opts.walletId,
|
|
2347
|
+
chooser: opts.chooser,
|
|
2348
|
+
picker: c.picker,
|
|
2349
|
+
fastPath: fast,
|
|
2350
|
+
pick: fast ? (l) => chooseWallet(l) : pickWallet,
|
|
2351
|
+
onAttempt: c.onAttempt,
|
|
2352
|
+
challengeUrl: c.challengeUrl,
|
|
2353
|
+
verifyUrl: c.verifyUrl,
|
|
2354
|
+
challengeNeedsIdentityKey: c.challengeNeedsIdentityKey,
|
|
2355
|
+
post: postJson2
|
|
2356
|
+
});
|
|
2357
|
+
if (!r) {
|
|
2358
|
+
setState((s) => ({ ...s, status: s.identityKey && s.signedIn ? "connected" : "idle" }));
|
|
1570
2359
|
return null;
|
|
1571
2360
|
}
|
|
1572
|
-
const cw =
|
|
1573
|
-
let { handle = null, paymail = null } = cw;
|
|
1574
|
-
let signedIn = false;
|
|
1575
|
-
if (cw.wallet && c.challengeUrl && c.verifyUrl) {
|
|
1576
|
-
const r = await signInWithBwalletX({
|
|
1577
|
-
wallet: cw.wallet,
|
|
1578
|
-
challenge: (identityKey) => postJson2(c.challengeUrl, { identityKey })
|
|
1579
|
-
});
|
|
1580
|
-
const v = await atStage("verify", () => postJson2(c.verifyUrl, { identityKey: r.identityKey, nonce: r.nonce, signature: r.signature }));
|
|
1581
|
-
handle = v.handle ?? handle;
|
|
1582
|
-
paymail = v.paymail ?? paymail;
|
|
1583
|
-
signedIn = true;
|
|
1584
|
-
}
|
|
2361
|
+
const cw = r.connected;
|
|
1585
2362
|
payer.current = cw.payer;
|
|
1586
2363
|
setState({
|
|
1587
2364
|
status: "connected",
|
|
1588
|
-
identityKey:
|
|
1589
|
-
handle,
|
|
1590
|
-
paymail,
|
|
2365
|
+
identityKey: r.identityKey,
|
|
2366
|
+
handle: r.handle,
|
|
2367
|
+
paymail: r.paymail,
|
|
1591
2368
|
wallet: cw.wallet,
|
|
1592
2369
|
walletId: cw.optionId,
|
|
2370
|
+
walletType: r.walletType,
|
|
1593
2371
|
transitional: !!cw.transitional,
|
|
1594
|
-
signedIn,
|
|
2372
|
+
signedIn: r.signedIn,
|
|
1595
2373
|
error: null
|
|
1596
2374
|
});
|
|
1597
|
-
return cw;
|
|
2375
|
+
return { ...cw, identityKey: r.identityKey, handle: r.handle ?? void 0, paymail: r.paymail ?? void 0 };
|
|
1598
2376
|
} catch (e) {
|
|
1599
2377
|
if (e?.message === "cancelled") {
|
|
1600
|
-
setState((s) => ({ ...s, status: s.identityKey ? "connected" : "idle" }));
|
|
2378
|
+
setState((s) => ({ ...s, status: s.identityKey && s.signedIn ? "connected" : "idle" }));
|
|
1601
2379
|
return null;
|
|
1602
2380
|
}
|
|
1603
2381
|
throw fail(e);
|
|
@@ -1613,6 +2391,17 @@ function BwalletXProvider(p) {
|
|
|
1613
2391
|
payer.current = null;
|
|
1614
2392
|
setState(empty);
|
|
1615
2393
|
}, [state.walletId]);
|
|
2394
|
+
(0, import_react.useEffect)(() => {
|
|
2395
|
+
const key = state.identityKey;
|
|
2396
|
+
if (!key || state.paymail || p.lookupUrl === "off") return;
|
|
2397
|
+
let live = true;
|
|
2398
|
+
void lookupPaymail(key, p.lookupUrl).then((pm) => {
|
|
2399
|
+
if (live && pm) setState((s) => s.identityKey === key && !s.paymail ? { ...s, paymail: pm } : s);
|
|
2400
|
+
});
|
|
2401
|
+
return () => {
|
|
2402
|
+
live = false;
|
|
2403
|
+
};
|
|
2404
|
+
}, [state.identityKey, state.paymail, p.lookupUrl]);
|
|
1616
2405
|
const need = () => {
|
|
1617
2406
|
if (!payer.current) throw new PaymentError("NOT_SUPPORTED", "Connect a wallet first (connect()).");
|
|
1618
2407
|
return payer.current;
|
|
@@ -1637,6 +2426,24 @@ function useBwalletX() {
|
|
|
1637
2426
|
if (!v) throw new Error("useBwalletX() must be used inside <BwalletXProvider>");
|
|
1638
2427
|
return v;
|
|
1639
2428
|
}
|
|
2429
|
+
function accountFor(v) {
|
|
2430
|
+
const status = signinStateFor(v);
|
|
2431
|
+
const signedIn = status === "signed-in";
|
|
2432
|
+
return {
|
|
2433
|
+
status,
|
|
2434
|
+
identityKey: signedIn ? v.identityKey : null,
|
|
2435
|
+
paymail: signedIn ? v.paymail : null,
|
|
2436
|
+
handle: signedIn ? v.handle : null,
|
|
2437
|
+
label: signedIn ? accountLabel(v) : null,
|
|
2438
|
+
avatarUrl: signedIn ? avatarUrlFor(v.identityKey) : null,
|
|
2439
|
+
error: status === "error" ? signInErrorText(v.error) : null,
|
|
2440
|
+
login: () => v.connect().then(() => void 0, () => void 0),
|
|
2441
|
+
logout: () => v.disconnect()
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
function useBwalletXAccount() {
|
|
2445
|
+
return accountFor(useBwalletX());
|
|
2446
|
+
}
|
|
1640
2447
|
var BwalletxProvider = BwalletXProvider;
|
|
1641
2448
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1642
2449
|
0 && (module.exports = {
|
|
@@ -1644,10 +2451,14 @@ var BwalletxProvider = BwalletXProvider;
|
|
|
1644
2451
|
BwalletxProvider,
|
|
1645
2452
|
BwalletxSignin,
|
|
1646
2453
|
PaymentError,
|
|
2454
|
+
accountFor,
|
|
2455
|
+
autoChoice,
|
|
1647
2456
|
brc100Option,
|
|
1648
2457
|
discoverWallets,
|
|
1649
2458
|
handcashAdapter,
|
|
1650
2459
|
pairingOption,
|
|
1651
|
-
|
|
2460
|
+
signinStateFor,
|
|
2461
|
+
useBwalletX,
|
|
2462
|
+
useBwalletXAccount
|
|
1652
2463
|
});
|
|
1653
2464
|
//# sourceMappingURL=react.cjs.map
|