@bwalletx/connect 0.5.6 → 0.7.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/CHANGELOG.md +28 -0
- package/README.md +22 -2
- package/dist/bwalletx-connect.global.js +15 -10
- package/dist/bwalletx-connect.global.js.map +1 -1
- package/dist/{choice-DCh-tgpT.d.cts → choice-B97AjqF4.d.cts} +87 -3
- package/dist/{choice-DCh-tgpT.d.ts → choice-B97AjqF4.d.ts} +87 -3
- package/dist/{chunk-LS6AYEBO.js → chunk-6EFFOIHK.js} +596 -41
- package/dist/chunk-6EFFOIHK.js.map +1 -0
- package/dist/{chunk-34DKBQNN.js → chunk-DIKJFKND.js} +22 -5
- package/dist/chunk-DIKJFKND.js.map +1 -0
- package/dist/{chunk-IFGOAROM.js → chunk-TNRI7ARM.js} +14 -63
- package/dist/chunk-TNRI7ARM.js.map +1 -0
- package/dist/{element-D9Zl-x12.d.cts → element-BXEoOdXY.d.cts} +11 -4
- package/dist/{element-D9Zl-x12.d.ts → element-BXEoOdXY.d.ts} +11 -4
- package/dist/element.cjs +480 -50
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +7 -2
- package/dist/element.d.ts +7 -2
- package/dist/element.js +8 -2
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +676 -100
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +169 -11
- package/dist/index.d.ts +169 -11
- package/dist/index.js +92 -6
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +683 -103
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +43 -4
- package/dist/react.d.ts +43 -4
- package/dist/react.js +147 -12
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-34DKBQNN.js.map +0 -1
- package/dist/chunk-IFGOAROM.js.map +0 -1
- package/dist/chunk-LS6AYEBO.js.map +0 -1
package/dist/element.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var define_exports = {};
|
|
|
32
32
|
__export(define_exports, {
|
|
33
33
|
BUTTON_TAG: () => BUTTON_TAG,
|
|
34
34
|
CONNECTED_KEY: () => CONNECTED_KEY,
|
|
35
|
+
CONTINUE_OTHER_LABEL: () => CONTINUE_OTHER_LABEL,
|
|
35
36
|
GET_URL: () => GET_URL,
|
|
36
37
|
MENU_CSS: () => MENU_CSS,
|
|
37
38
|
PENDING_LABEL: () => PENDING_LABEL,
|
|
@@ -39,6 +40,7 @@ __export(define_exports, {
|
|
|
39
40
|
PENDING_NOTE: () => PENDING_NOTE,
|
|
40
41
|
SIGNIN_CSS: () => SIGNIN_CSS,
|
|
41
42
|
SIGNIN_TAG: () => SIGNIN_TAG,
|
|
43
|
+
SWITCH_NOTE: () => SWITCH_NOTE,
|
|
42
44
|
WEB_WALLET_URL: () => WEB_WALLET_URL,
|
|
43
45
|
_resetBwalletxButtons: () => _resetBwalletxButtons,
|
|
44
46
|
buildAuthorizeUrl: () => buildAuthorizeUrl,
|
|
@@ -48,6 +50,7 @@ __export(define_exports, {
|
|
|
48
50
|
getUrlFor: () => getUrlFor,
|
|
49
51
|
handleLabel: () => handleLabel,
|
|
50
52
|
markSvg: () => markSvg,
|
|
53
|
+
migrateConnectedKey: () => migrateConnectedKey,
|
|
51
54
|
signinText: () => signinText,
|
|
52
55
|
stateFor: () => stateFor
|
|
53
56
|
});
|
|
@@ -736,23 +739,8 @@ async function discoverWallets(o = {}) {
|
|
|
736
739
|
return out.map((w, i) => ({ w, i })).sort((a, b) => rank(a.w) - rank(b.w) || a.i - b.i).map((x) => x.w);
|
|
737
740
|
}
|
|
738
741
|
var DESKTOP_OPTION_ID = "desktop";
|
|
739
|
-
function
|
|
740
|
-
|
|
741
|
-
id: DESKTOP_OPTION_ID,
|
|
742
|
-
name: "Desktop wallet",
|
|
743
|
-
kind: "brc100",
|
|
744
|
-
isBwalletX: false,
|
|
745
|
-
substrate: "json-api",
|
|
746
|
-
async connect(c) {
|
|
747
|
-
const [json, cicada] = await Promise.all([probe("json-api", ms), probe("Cicada", ms)]);
|
|
748
|
-
const w = json ?? cicada;
|
|
749
|
-
if (!w) throw Object.assign(new Error("No desktop wallet answered on this computer"), { code: "NOT_FOUND" });
|
|
750
|
-
opt.wallet = w;
|
|
751
|
-
opt.substrate = json ? "json-api" : "cicada";
|
|
752
|
-
return brc100Connected(w, opt, c);
|
|
753
|
-
}
|
|
754
|
-
};
|
|
755
|
-
return opt;
|
|
742
|
+
function isLocalDesktop(w) {
|
|
743
|
+
return w.substrate === "json-api" || w.substrate === "cicada" || w.id === "localhost:3321" || w.id === "localhost:3301" || w.id === DESKTOP_OPTION_ID;
|
|
756
744
|
}
|
|
757
745
|
|
|
758
746
|
// src/account.ts
|
|
@@ -789,6 +777,7 @@ function isUserRejection(e) {
|
|
|
789
777
|
}
|
|
790
778
|
function signInErrorText(e) {
|
|
791
779
|
if (isUserRejection(e)) return "Sign-in cancelled";
|
|
780
|
+
if (e?.name === "MetanetUnreachableError") return e.message;
|
|
792
781
|
const code = e?.code;
|
|
793
782
|
if (code === "NOT_FOUND") return "bWalletX was not found in this browser";
|
|
794
783
|
const msg = e instanceof Error ? e.message : String(e ?? "");
|
|
@@ -822,6 +811,171 @@ function rememberIdentity(key) {
|
|
|
822
811
|
}
|
|
823
812
|
}
|
|
824
813
|
|
|
814
|
+
// src/metanet.ts
|
|
815
|
+
var import_sdk5 = require("@bsv/sdk");
|
|
816
|
+
|
|
817
|
+
// src/device.ts
|
|
818
|
+
function isTouchDevice(env = globalThis) {
|
|
819
|
+
const nav = env.navigator;
|
|
820
|
+
if (!nav) return false;
|
|
821
|
+
const ua = nav.userAgent ?? "";
|
|
822
|
+
if (/Android|iPhone|iPad|iPod|Mobile|Silk|Kindle/i.test(ua)) return true;
|
|
823
|
+
if (/Macintosh/.test(ua) && (nav.maxTouchPoints ?? 0) > 1) return true;
|
|
824
|
+
try {
|
|
825
|
+
return !!env.matchMedia?.("(pointer: coarse)").matches && !env.matchMedia?.("(hover: hover)").matches;
|
|
826
|
+
} catch {
|
|
827
|
+
return false;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// src/metanet.ts
|
|
832
|
+
var METANET_TYPE = "metanet";
|
|
833
|
+
var METANET_OPTION_ID = METANET_TYPE;
|
|
834
|
+
var METANET_LABEL = "Use Metanet Desktop or another BRC-100 wallet";
|
|
835
|
+
var METANET_NOTE = "Metanet Desktop must be running on this computer";
|
|
836
|
+
var METANET_REMEMBERED_NOTE = "Signs in with Metanet Desktop on this computer.";
|
|
837
|
+
var NOT_YOU_LABEL = "Not you? Use another wallet";
|
|
838
|
+
var METANET_UNREACHABLE = "We couldn\u2019t reach Metanet Desktop. Open it, then try again.";
|
|
839
|
+
var METANET_ANSWER_MS = 4e3;
|
|
840
|
+
var METANET_SILENT_MS = 2e3;
|
|
841
|
+
var MetanetUnreachableError = class extends Error {
|
|
842
|
+
code = "NOT_FOUND";
|
|
843
|
+
constructor() {
|
|
844
|
+
super(METANET_UNREACHABLE);
|
|
845
|
+
this.name = "MetanetUnreachableError";
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
function inAnyFrame(win = typeof window === "undefined" ? void 0 : window) {
|
|
849
|
+
if (!win) return false;
|
|
850
|
+
try {
|
|
851
|
+
return win.top !== win.self;
|
|
852
|
+
} catch {
|
|
853
|
+
return true;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function metanetOffered(env = {}) {
|
|
857
|
+
const win = env.window ?? (typeof window === "undefined" ? void 0 : window);
|
|
858
|
+
if (!win) return false;
|
|
859
|
+
const nav = env.navigator ?? (typeof navigator === "undefined" ? void 0 : navigator);
|
|
860
|
+
const mm = env.matchMedia ?? (typeof matchMedia === "undefined" ? void 0 : (q) => matchMedia(q));
|
|
861
|
+
return !isTouchDevice({ navigator: nav, matchMedia: mm }) && !inAnyFrame(win);
|
|
862
|
+
}
|
|
863
|
+
function client(o) {
|
|
864
|
+
if (o.make) return o.make();
|
|
865
|
+
const originator = o.originator ?? (typeof location !== "undefined" && location.host ? location.host : void 0);
|
|
866
|
+
return new import_sdk5.WalletClient("json-api", originator);
|
|
867
|
+
}
|
|
868
|
+
async function answers(wallet) {
|
|
869
|
+
return Promise.resolve().then(() => wallet.getVersion({})).then((v) => typeof v?.version === "string", () => false);
|
|
870
|
+
}
|
|
871
|
+
async function connectMetanetDesktop(o = {}) {
|
|
872
|
+
let wallet;
|
|
873
|
+
try {
|
|
874
|
+
wallet = client(o);
|
|
875
|
+
} catch {
|
|
876
|
+
throw new MetanetUnreachableError();
|
|
877
|
+
}
|
|
878
|
+
let timer;
|
|
879
|
+
const ok = await Promise.race([answers(wallet), new Promise((r) => timer = setTimeout(() => r(false), o.timeoutMs ?? METANET_ANSWER_MS))]);
|
|
880
|
+
clearTimeout(timer);
|
|
881
|
+
if (!ok) throw new MetanetUnreachableError();
|
|
882
|
+
return wallet;
|
|
883
|
+
}
|
|
884
|
+
function metanetOption(o = {}) {
|
|
885
|
+
const opt = {
|
|
886
|
+
id: METANET_OPTION_ID,
|
|
887
|
+
name: METANET_LABEL,
|
|
888
|
+
kind: "brc100",
|
|
889
|
+
isBwalletX: false,
|
|
890
|
+
substrate: "json-api",
|
|
891
|
+
async connect() {
|
|
892
|
+
const wallet = await connectMetanetDesktop(o);
|
|
893
|
+
opt.wallet = wallet;
|
|
894
|
+
await waitIfLocked(wallet);
|
|
895
|
+
const { publicKey } = await wallet.getPublicKey({ identityKey: true });
|
|
896
|
+
return { wallet, payer: wallet, identityKey: publicKey, optionId: opt.id, kind: "brc100" };
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
return opt;
|
|
900
|
+
}
|
|
901
|
+
var KNOWN_KEYS_KEY = "bwx:known-keys";
|
|
902
|
+
var KEY_RE = /^0[23][0-9a-f]{64}$/;
|
|
903
|
+
var MAX_KNOWN = 8;
|
|
904
|
+
function knownIdentityKeys() {
|
|
905
|
+
try {
|
|
906
|
+
const v = JSON.parse(globalThis.localStorage?.getItem(KNOWN_KEYS_KEY) || "[]");
|
|
907
|
+
return Array.isArray(v) ? v.filter((k) => typeof k === "string" && KEY_RE.test(k)) : [];
|
|
908
|
+
} catch {
|
|
909
|
+
return [];
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
function isKnownIdentityKey(key) {
|
|
913
|
+
if (!key) return false;
|
|
914
|
+
const k = key.toLowerCase();
|
|
915
|
+
if (!KEY_RE.test(k)) return false;
|
|
916
|
+
return knownIdentityKeys().includes(k) || rememberedIdentity()?.toLowerCase() === k;
|
|
917
|
+
}
|
|
918
|
+
function rememberKnownIdentityKey(key) {
|
|
919
|
+
const k = (key ?? "").toLowerCase();
|
|
920
|
+
if (!KEY_RE.test(k)) return;
|
|
921
|
+
try {
|
|
922
|
+
const list = [k, ...knownIdentityKeys().filter((x) => x !== k)].slice(0, MAX_KNOWN);
|
|
923
|
+
globalThis.localStorage?.setItem(KNOWN_KEYS_KEY, JSON.stringify(list));
|
|
924
|
+
} catch {
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
var triedThisLoad = false;
|
|
928
|
+
function silentAttemptUsed() {
|
|
929
|
+
return triedThisLoad;
|
|
930
|
+
}
|
|
931
|
+
function claimSilentAttempt() {
|
|
932
|
+
if (triedThisLoad) return false;
|
|
933
|
+
triedThisLoad = true;
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
async function trySilentMetanet(o = {}) {
|
|
937
|
+
if (!metanetOffered()) return { ok: false, stop: "not-here" };
|
|
938
|
+
if (!claimSilentAttempt()) return { ok: false, stop: "used" };
|
|
939
|
+
const run = async () => {
|
|
940
|
+
let wallet;
|
|
941
|
+
try {
|
|
942
|
+
wallet = client(o);
|
|
943
|
+
} catch {
|
|
944
|
+
return { ok: false, stop: "unreachable" };
|
|
945
|
+
}
|
|
946
|
+
if (!await answers(wallet)) return { ok: false, stop: "unreachable" };
|
|
947
|
+
const unlocked = await Promise.resolve().then(() => wallet.isAuthenticated({})).then((r2) => r2?.authenticated === true, () => false);
|
|
948
|
+
if (!unlocked) return { ok: false, stop: "locked" };
|
|
949
|
+
const identityKey = await Promise.resolve().then(() => wallet.getPublicKey({ identityKey: true })).then((r2) => r2.publicKey, () => null);
|
|
950
|
+
if (!identityKey) return { ok: false, stop: "unreachable" };
|
|
951
|
+
if (!isKnownIdentityKey(identityKey)) return { ok: false, stop: "unknown-key" };
|
|
952
|
+
return { ok: true, wallet, identityKey };
|
|
953
|
+
};
|
|
954
|
+
let timer;
|
|
955
|
+
const r = await Promise.race([
|
|
956
|
+
run().catch(() => ({ ok: false, stop: "unreachable" })),
|
|
957
|
+
new Promise((res) => timer = setTimeout(() => res({ ok: false, stop: "slow" }), o.windowMs ?? METANET_SILENT_MS))
|
|
958
|
+
]);
|
|
959
|
+
clearTimeout(timer);
|
|
960
|
+
return r;
|
|
961
|
+
}
|
|
962
|
+
var warnedLogout = false;
|
|
963
|
+
var isProduction = () => {
|
|
964
|
+
try {
|
|
965
|
+
return globalThis.process?.env?.NODE_ENV === "production";
|
|
966
|
+
} catch {
|
|
967
|
+
return false;
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
function warnIfNoLogout(o) {
|
|
971
|
+
if (!o.verifyUrl || o.logoutUrl || warnedLogout || isProduction() || typeof console === "undefined") return false;
|
|
972
|
+
warnedLogout = true;
|
|
973
|
+
console.warn(
|
|
974
|
+
`@bwalletx/connect: ${o.where} has a verify URL but no logout URL. Set it: "Not you? Use another wallet" and Disconnect must be able to end a session the verify route created.`
|
|
975
|
+
);
|
|
976
|
+
return true;
|
|
977
|
+
}
|
|
978
|
+
|
|
825
979
|
// src/choice.ts
|
|
826
980
|
var BWALLETX_TYPE = "bwalletx";
|
|
827
981
|
function substrateOf(o) {
|
|
@@ -868,6 +1022,64 @@ function attemptFor(option, outcome, startedAt, now = Date.now()) {
|
|
|
868
1022
|
ms: Math.max(0, Math.round(now - startedAt))
|
|
869
1023
|
};
|
|
870
1024
|
}
|
|
1025
|
+
function reportAttempt(hook, a) {
|
|
1026
|
+
if (!hook) return;
|
|
1027
|
+
try {
|
|
1028
|
+
hook(Object.freeze({ walletType: a.walletType, substrate: a.substrate, outcome: a.outcome, ms: a.ms }));
|
|
1029
|
+
} catch {
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
var WALLET_CHOICE_KEY = "bwx:wallet";
|
|
1033
|
+
var SUBSTRATES = ["extension", "in-app", "xdm", "injected", "window.CWI", "json-api", "cicada", "pairing", "adapter"];
|
|
1034
|
+
var TYPE_RE = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
1035
|
+
var KEY_LIKE = /^(0[23][0-9a-f]{64}|04[0-9a-f]{128}|[13][1-9A-HJ-NP-Za-km-z]{25,34})$/i;
|
|
1036
|
+
function isRememberable(o) {
|
|
1037
|
+
const t = walletTypeOf(o);
|
|
1038
|
+
return substrateOf(o) !== "window.CWI" && TYPE_RE.test(t) && !KEY_LIKE.test(t);
|
|
1039
|
+
}
|
|
1040
|
+
function isExplicitPick(o, reason) {
|
|
1041
|
+
return o.isBwalletX || reason === "chooser" || reason === "requested" || reason === "remembered";
|
|
1042
|
+
}
|
|
1043
|
+
function rememberWalletChoice(o) {
|
|
1044
|
+
try {
|
|
1045
|
+
if (!o) return void globalThis.localStorage?.removeItem(WALLET_CHOICE_KEY);
|
|
1046
|
+
if (!isRememberable(o)) return;
|
|
1047
|
+
const v = { type: walletTypeOf(o), substrate: substrateOf(o), v: 1 };
|
|
1048
|
+
globalThis.localStorage?.setItem(WALLET_CHOICE_KEY, JSON.stringify(v));
|
|
1049
|
+
} catch {
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
function rememberedWalletChoice() {
|
|
1053
|
+
try {
|
|
1054
|
+
const raw = globalThis.localStorage?.getItem(WALLET_CHOICE_KEY);
|
|
1055
|
+
if (!raw) return null;
|
|
1056
|
+
const j = JSON.parse(raw);
|
|
1057
|
+
if (typeof j.type !== "string" || !TYPE_RE.test(j.type) || KEY_LIKE.test(j.type)) return null;
|
|
1058
|
+
if (!SUBSTRATES.includes(j.substrate) || j.substrate === "window.CWI") return null;
|
|
1059
|
+
return { type: j.type, substrate: j.substrate };
|
|
1060
|
+
} catch {
|
|
1061
|
+
return null;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
function forgetWalletChoice() {
|
|
1065
|
+
rememberWalletChoice(null);
|
|
1066
|
+
}
|
|
1067
|
+
function needsBwalletX(type) {
|
|
1068
|
+
return !!type && type !== BWALLETX_TYPE;
|
|
1069
|
+
}
|
|
1070
|
+
function tapGoesElsewhere(type) {
|
|
1071
|
+
return needsBwalletX(type) && type !== METANET_TYPE && type !== DESKTOP_OPTION_ID;
|
|
1072
|
+
}
|
|
1073
|
+
function isLocalDesktopType(r) {
|
|
1074
|
+
return r.type === METANET_TYPE || r.type === DESKTOP_OPTION_ID || r.substrate === "json-api" || r.substrate === "cicada";
|
|
1075
|
+
}
|
|
1076
|
+
function matchRemembered(list, r) {
|
|
1077
|
+
if (isLocalDesktopType(r)) return null;
|
|
1078
|
+
if (r.type === BWALLETX_TYPE) {
|
|
1079
|
+
return (r.substrate === "pairing" ? list.find((o) => o.kind === "pairing") : list.find(isPresentBwalletX)) ?? null;
|
|
1080
|
+
}
|
|
1081
|
+
return list.find((o) => !o.isBwalletX && !isLocalDesktop(o) && isRememberable(o) && walletTypeOf(o) === r.type) ?? null;
|
|
1082
|
+
}
|
|
871
1083
|
|
|
872
1084
|
// src/modal.ts
|
|
873
1085
|
var import_qrcode_generator = __toESM(require("qrcode-generator"), 1);
|
|
@@ -929,18 +1141,6 @@ function qrSvg(text) {
|
|
|
929
1141
|
q.make();
|
|
930
1142
|
return q.createSvgTag({ cellSize: 4, margin: 2, scalable: true });
|
|
931
1143
|
}
|
|
932
|
-
function isTouchDevice(env = globalThis) {
|
|
933
|
-
const nav = env.navigator;
|
|
934
|
-
if (!nav) return false;
|
|
935
|
-
const ua = nav.userAgent ?? "";
|
|
936
|
-
if (/Android|iPhone|iPad|iPod|Mobile|Silk|Kindle/i.test(ua)) return true;
|
|
937
|
-
if (/Macintosh/.test(ua) && (nav.maxTouchPoints ?? 0) > 1) return true;
|
|
938
|
-
try {
|
|
939
|
-
return !!env.matchMedia?.("(pointer: coarse)").matches && !env.matchMedia?.("(hover: hover)").matches;
|
|
940
|
-
} catch {
|
|
941
|
-
return false;
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
1144
|
function pairingModal(o = {}) {
|
|
945
1145
|
const ac = new AbortController();
|
|
946
1146
|
let ui = null;
|
|
@@ -1070,10 +1270,11 @@ function chooseWallet(options, o = {}) {
|
|
|
1070
1270
|
row2.onclick = () => done(w);
|
|
1071
1271
|
list.append(row2);
|
|
1072
1272
|
}
|
|
1073
|
-
if (o.desktop !== false && !options.some((w) => w.substrate === "json-api" || w.substrate === "cicada")) {
|
|
1273
|
+
if (o.desktop !== false && metanetOffered() && !options.some((w) => w.substrate === "json-api" || w.substrate === "cicada")) {
|
|
1074
1274
|
if (!here && !sep) list.append(el("p", "bwx-sep", "Or use"));
|
|
1075
|
-
const d =
|
|
1076
|
-
const row2 = optionRow(d, false,
|
|
1275
|
+
const d = metanetOption();
|
|
1276
|
+
const row2 = optionRow(d, false, METANET_NOTE);
|
|
1277
|
+
row2.title = "Connects to Metanet Desktop on this computer, never to a browser extension.";
|
|
1077
1278
|
row2.onclick = () => done(d);
|
|
1078
1279
|
list.append(row2);
|
|
1079
1280
|
}
|
|
@@ -1091,7 +1292,7 @@ function chooseWallet(options, o = {}) {
|
|
|
1091
1292
|
}
|
|
1092
1293
|
|
|
1093
1294
|
// src/access-client.ts
|
|
1094
|
-
var
|
|
1295
|
+
var import_sdk6 = require("@bsv/sdk");
|
|
1095
1296
|
|
|
1096
1297
|
// src/access.ts
|
|
1097
1298
|
var ACCESS_PROTOCOL = [2, "bwalletx access"];
|
|
@@ -1229,7 +1430,7 @@ async function proveHoldings(wallet, challenge, tokenId, basket = "bsv21") {
|
|
|
1229
1430
|
const counterparty = ci.counterparty ?? "self";
|
|
1230
1431
|
for (const protocolID of ci.protocolID ? [ci.protocolID] : [ONESAT, LEGACY_ONESAT]) {
|
|
1231
1432
|
const { publicKey } = await wallet.getPublicKey({ protocolID, keyID: ci.keyID, counterparty });
|
|
1232
|
-
const h =
|
|
1433
|
+
const h = import_sdk6.Utils.toHex(import_sdk6.Hash.hash160(import_sdk6.PublicKey.fromString(publicKey).encode(true)));
|
|
1233
1434
|
if (want && h !== want) continue;
|
|
1234
1435
|
const existing = byKey.get(publicKey);
|
|
1235
1436
|
if (existing) {
|
|
@@ -1275,6 +1476,48 @@ async function requestAccess(o) {
|
|
|
1275
1476
|
}
|
|
1276
1477
|
}
|
|
1277
1478
|
|
|
1479
|
+
// src/connect-flow.ts
|
|
1480
|
+
async function runSilentMetanet(o = {}) {
|
|
1481
|
+
const s = await trySilentMetanet(o);
|
|
1482
|
+
if (!s.ok || o.isAbandoned?.()) return null;
|
|
1483
|
+
const started = Date.now();
|
|
1484
|
+
const opt = metanetOption(o);
|
|
1485
|
+
opt.wallet = s.wallet;
|
|
1486
|
+
const cw = { wallet: s.wallet, payer: s.wallet, identityKey: s.identityKey, optionId: METANET_OPTION_ID, kind: "brc100" };
|
|
1487
|
+
const done = (signedIn, handle = null, paymail = null) => {
|
|
1488
|
+
rememberWalletChoice(opt);
|
|
1489
|
+
if (signedIn) rememberKnownIdentityKey(s.identityKey);
|
|
1490
|
+
reportAttempt(o.onAttempt, attemptFor(opt, signedIn ? "signed-in" : "connected", started));
|
|
1491
|
+
return { connected: cw, identityKey: s.identityKey, handle, paymail, signedIn, walletType: walletTypeOf(opt), substrate: "json-api", reason: "remembered" };
|
|
1492
|
+
};
|
|
1493
|
+
if (o.sessionKey !== void 0 && o.sessionKey !== null) {
|
|
1494
|
+
return o.sessionKey.toLowerCase() === s.identityKey.toLowerCase() ? done(true) : null;
|
|
1495
|
+
}
|
|
1496
|
+
if (!o.challengeUrl || !o.verifyUrl || !o.post) return done(false);
|
|
1497
|
+
if (!isKnownIdentityKey(s.identityKey)) return null;
|
|
1498
|
+
try {
|
|
1499
|
+
o.onSignIn?.();
|
|
1500
|
+
const post = o.post;
|
|
1501
|
+
const { result, verified } = await signInAndVerify({
|
|
1502
|
+
wallet: s.wallet,
|
|
1503
|
+
identityKey: s.identityKey,
|
|
1504
|
+
skipWaitForAuthentication: true,
|
|
1505
|
+
// trySilentMetanet just saw it unlocked
|
|
1506
|
+
challenge: (key) => post(o.challengeUrl, key ? { identityKey: key } : {}),
|
|
1507
|
+
verify: (r) => post(o.verifyUrl, { identityKey: r.identityKey, nonce: r.nonce, signature: r.signature })
|
|
1508
|
+
});
|
|
1509
|
+
if (o.isAbandoned?.()) {
|
|
1510
|
+
await Promise.resolve(o.logout?.()).catch(() => void 0);
|
|
1511
|
+
return null;
|
|
1512
|
+
}
|
|
1513
|
+
rememberIdentity(result.identityKey);
|
|
1514
|
+
return done(true, verified.handle ?? null, verified.paymail ?? null);
|
|
1515
|
+
} catch (e) {
|
|
1516
|
+
reportAttempt(o.onAttempt, attemptFor(opt, outcomeOf(e), started));
|
|
1517
|
+
return null;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1278
1521
|
// src/element.ts
|
|
1279
1522
|
var SIGNIN_TAG = "bwalletx-signin";
|
|
1280
1523
|
var GET_URL = "https://bwalletx.com/get";
|
|
@@ -1340,6 +1583,11 @@ button[aria-busy=true]{cursor:wait}
|
|
|
1340
1583
|
/* "Other wallets" (0.5.6): one small link under the button */
|
|
1341
1584
|
.ow{margin:8px 0 0;width:0;min-width:100%;font-size:12px;line-height:1.4;color:#71717a;text-align:center}
|
|
1342
1585
|
:host([theme=dark]) .ow,:host-context(.dark) .ow{color:#a1a1aa}
|
|
1586
|
+
.sw{margin:8px 0 0;width:0;min-width:100%;font-size:12px;line-height:1.4;color:#a16207;text-align:center}
|
|
1587
|
+
:host([theme=dark]) .sw,:host-context(.dark) .sw{color:#fcd34d}
|
|
1588
|
+
.mn{margin:8px 0 0;width:0;min-width:100%;font-size:12px;line-height:1.4;color:#71717a;text-align:center}
|
|
1589
|
+
.mn small{display:block;font-size:11px;opacity:.85}
|
|
1590
|
+
:host([theme=dark]) .mn,:host-context(.dark) .mn{color:#a1a1aa}
|
|
1343
1591
|
.lnk{all:unset;cursor:pointer;color:inherit;font-weight:600;text-decoration:underline;text-underline-offset:2px}
|
|
1344
1592
|
.lnk:focus-visible{outline:2px solid #b45309;outline-offset:2px;border-radius:2px}
|
|
1345
1593
|
/* error: one line plus Retry */
|
|
@@ -1378,6 +1626,11 @@ var MENU_CSS = `
|
|
|
1378
1626
|
var PENDING_NOTE = "Sign in: shares only your public identity key, moves no money";
|
|
1379
1627
|
var PENDING_LABEL = "Approve in bWalletX\u2026";
|
|
1380
1628
|
var PENDING_LABEL_OTHER = "Approve in your wallet\u2026";
|
|
1629
|
+
var SWITCH_NOTE = "Some features need bWalletX";
|
|
1630
|
+
var CONTINUE_OTHER_LABEL = "Continue with your wallet";
|
|
1631
|
+
function walletGlyph(px) {
|
|
1632
|
+
return `<svg viewBox="0 0 24 24" width="${px}" height="${px}" aria-hidden="true" focusable="false" fill="none" stroke="#FFD24D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 7V5a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2"/><path d="M3 6v12a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-3"/></svg>`;
|
|
1633
|
+
}
|
|
1381
1634
|
function b64url2(bytes) {
|
|
1382
1635
|
let s = "";
|
|
1383
1636
|
for (const b of bytes) s += String.fromCharCode(b);
|
|
@@ -1417,7 +1670,7 @@ async function postAccess(url, body) {
|
|
|
1417
1670
|
const res = await fetch(url, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
1418
1671
|
return res.json().catch(() => ({ error: "bad_request", message: `HTTP ${res.status}` }));
|
|
1419
1672
|
}
|
|
1420
|
-
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"];
|
|
1673
|
+
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", "wallet-type", "switch-note", "remember", "metanet"];
|
|
1421
1674
|
var STATES = ["signed-out", "restoring", "pending", "signed-in", "error"];
|
|
1422
1675
|
function createClass() {
|
|
1423
1676
|
return class BwalletxSigninElement extends HTMLElement {
|
|
@@ -1432,14 +1685,21 @@ function createClass() {
|
|
|
1432
1685
|
#error = "";
|
|
1433
1686
|
#restored = false;
|
|
1434
1687
|
#pendingLabel = PENDING_LABEL;
|
|
1688
|
+
/** The wallet type signed in with here (0.6.0), for the Switch note. */
|
|
1689
|
+
#signedType = null;
|
|
1435
1690
|
#menu = null;
|
|
1691
|
+
/** (0.7.0) A Metanet sign-in (tapped or silent) is in flight. */
|
|
1692
|
+
#viaMetanet = false;
|
|
1693
|
+
/** (0.7.0) Bumped by "Not you?": a Metanet sign-in started before it lands nobody. */
|
|
1694
|
+
#run = 0;
|
|
1436
1695
|
constructor() {
|
|
1437
1696
|
super();
|
|
1438
1697
|
this.#root = this.attachShadow({ mode: "open" });
|
|
1439
1698
|
}
|
|
1440
1699
|
connectedCallback() {
|
|
1700
|
+
if (!this.#controlled && this.#stateful) warnIfNoLogout({ verifyUrl: this.#attr("verify-url"), logoutUrl: this.#attr("logout-url"), where: "<bwalletx-signin>" });
|
|
1441
1701
|
this.#render();
|
|
1442
|
-
void this.#restore();
|
|
1702
|
+
void this.#restore().then(() => this.#silentMetanet());
|
|
1443
1703
|
}
|
|
1444
1704
|
disconnectedCallback() {
|
|
1445
1705
|
this.#menu?.close(false);
|
|
@@ -1545,10 +1805,11 @@ function createClass() {
|
|
|
1545
1805
|
const restoring = state === "restoring";
|
|
1546
1806
|
const error = state === "error";
|
|
1547
1807
|
const pendingLabel = this.#controlled ? PENDING_LABEL : this.#pendingLabel;
|
|
1548
|
-
const
|
|
1808
|
+
const elsewhere = state === "signed-out" && tapGoesElsewhere(this.#walletType());
|
|
1809
|
+
const text = pending ? pendingLabel : error ? "Retry" : elsewhere ? CONTINUE_OTHER_LABEL : signinText(this.#attr("label") ?? "signin", brand);
|
|
1549
1810
|
const subAttr = this.#attr("subtitle");
|
|
1550
1811
|
const subDefault = brand === "bWalletX" && (size === "lg" || size === "md") ? "also works with bWallet" : null;
|
|
1551
|
-
const subtitle = pending || error ? null : subAttr === null || subAttr === "" || subAttr === "on" ? subAttr === "on" ? "also works with bWallet" : subDefault : subAttr === "off" ? null : subAttr;
|
|
1812
|
+
const subtitle = pending || error || elsewhere ? null : subAttr === null || subAttr === "" || subAttr === "on" ? subAttr === "on" ? "also works with bWallet" : subDefault : subAttr === "off" ? null : subAttr;
|
|
1552
1813
|
const loading = this.hasAttribute("loading") || this.#busy || pending || restoring;
|
|
1553
1814
|
const disabled = this.hasAttribute("disabled");
|
|
1554
1815
|
const markPx = size === "icon" ? 24 : size === "sm" ? 15 : size === "md" ? 17 : 20;
|
|
@@ -1557,18 +1818,26 @@ function createClass() {
|
|
|
1557
1818
|
const webUrl = this.#attr("web-url") ?? WEB_WALLET_URL;
|
|
1558
1819
|
const errText = error ? (this.#controlled ? this.#attr("error") : this.#error) || "Sign-in failed" : "";
|
|
1559
1820
|
const label = size === "icon" ? "" : `<span class="txt" part="text"><b part="label">${esc(text)}</b>${subtitle ? `<small part="subtitle">${esc(subtitle)}</small>` : ""}</span>`;
|
|
1560
|
-
const mark = loading ? '<span class="spin" aria-hidden="true"></span>' : markSvg(markPx);
|
|
1821
|
+
const mark = loading ? '<span class="spin" aria-hidden="true"></span>' : elsewhere ? walletGlyph(markPx - 4) : markSvg(markPx);
|
|
1561
1822
|
const otherOff = this.#attr("other-wallets") === "off";
|
|
1562
1823
|
const otherLink = '<button type="button" class="lnk" data-act="other" part="other-wallets-link">Other wallets</button>';
|
|
1563
1824
|
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>` : "";
|
|
1564
1825
|
const ow = !this.#notFound && !pending && !restoring && this.#otherWalletsShown(size) ? `<p class="ow" part="other-wallets">${otherLink}</p>` : "";
|
|
1826
|
+
const sw = !pending && !restoring && this.#switchNoteShown(size) ? `<p class="sw" part="switch-note">${esc(SWITCH_NOTE)} \xB7 <button type="button" class="lnk" data-act="switch" part="switch-link">Switch</button></p>` : "";
|
|
1565
1827
|
const err = error ? `<p class="err" part="error" role="alert">${esc(errText)}</p>` : "";
|
|
1828
|
+
const mnHere = this.#metanetShown(size);
|
|
1829
|
+
const mnRemembered = mnHere && (pending ? this.#metanetPending() : !restoring && this.#walletType() === METANET_TYPE);
|
|
1830
|
+
const notYou = `<button type="button" class="lnk" data-act="not-you" part="not-you-link">${esc(NOT_YOU_LABEL)}</button>`;
|
|
1831
|
+
const mn = !mnHere ? "" : pending ? mnRemembered ? `<p class="mn" part="metanet">${esc(METANET_REMEMBERED_NOTE)} ${notYou}</p>` : "" : restoring ? "" : `<p class="mn" part="metanet"><button type="button" class="lnk" data-act="metanet" part="metanet-link" title="Connects to Metanet Desktop on this computer, never to a browser extension.">${esc(METANET_LABEL)}</button><small>${esc(METANET_NOTE)}</small>${mnRemembered ? `<small>${esc(METANET_REMEMBERED_NOTE)} ${notYou}</small>` : ""}</p>`;
|
|
1566
1832
|
const compact = size === "sm" || size === "icon";
|
|
1567
1833
|
const note = pending && !compact ? `<p class="note" part="note">${esc(PENDING_NOTE)}</p>` : "";
|
|
1568
1834
|
const status = pending ? `<span class="sr" role="status">${esc(pendingLabel)}</span>` : loading ? '<span class="sr" role="status">Connecting to bWalletX\u2026</span>' : "";
|
|
1569
|
-
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}`;
|
|
1835
|
+
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}${sw}${mn}${ow}${nf}`;
|
|
1570
1836
|
this.button.addEventListener("click", () => void this.#click());
|
|
1571
1837
|
for (const l of Array.from(this.#root.querySelectorAll("[data-act=other]"))) l.addEventListener("click", () => void this.otherWallets());
|
|
1838
|
+
this.#root.querySelector("[data-act=switch]")?.addEventListener("click", () => void this.switchToBwalletX());
|
|
1839
|
+
this.#root.querySelector("[data-act=metanet]")?.addEventListener("click", () => void this.useMetanet());
|
|
1840
|
+
this.#root.querySelector("[data-act=not-you]")?.addEventListener("click", () => this.notYou());
|
|
1572
1841
|
bindPrewarm(this.button);
|
|
1573
1842
|
if (pending && compact) this.#showTip(PENDING_NOTE);
|
|
1574
1843
|
else this.#hideTip();
|
|
@@ -1636,7 +1905,20 @@ function createClass() {
|
|
|
1636
1905
|
close(false);
|
|
1637
1906
|
void this.signOut();
|
|
1638
1907
|
});
|
|
1639
|
-
|
|
1908
|
+
const items = [copy, out];
|
|
1909
|
+
menu.append(who);
|
|
1910
|
+
if (needsBwalletX(this.#walletType())) {
|
|
1911
|
+
const sw = item("Switch to bWalletX", () => {
|
|
1912
|
+
close(false);
|
|
1913
|
+
void this.switchToBwalletX();
|
|
1914
|
+
});
|
|
1915
|
+
const line = document.createElement("div");
|
|
1916
|
+
line.className = "who";
|
|
1917
|
+
line.textContent = SWITCH_NOTE;
|
|
1918
|
+
menu.append(line, sw);
|
|
1919
|
+
items.unshift(sw);
|
|
1920
|
+
}
|
|
1921
|
+
menu.append(copy, out);
|
|
1640
1922
|
root.append(style, menu);
|
|
1641
1923
|
document.body.appendChild(host);
|
|
1642
1924
|
const place = () => {
|
|
@@ -1652,7 +1934,6 @@ function createClass() {
|
|
|
1652
1934
|
menu.style.top = `${below + h > vh && r.top - 8 - h > 0 ? r.top - 8 - h : below}px`;
|
|
1653
1935
|
};
|
|
1654
1936
|
place();
|
|
1655
|
-
const items = [copy, out];
|
|
1656
1937
|
const focusAt = (i) => items[(i + items.length) % items.length].focus();
|
|
1657
1938
|
const onKey = (e) => {
|
|
1658
1939
|
const i = items.indexOf(root.activeElement);
|
|
@@ -1707,6 +1988,8 @@ function createClass() {
|
|
|
1707
1988
|
if (this.#controlled) return;
|
|
1708
1989
|
const url = this.#attr("logout-url");
|
|
1709
1990
|
if (url) await fetch(url, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: "{}" }).catch(() => void 0);
|
|
1991
|
+
if (this.#remember) forgetWalletChoice();
|
|
1992
|
+
this.#signedType = null;
|
|
1710
1993
|
this.#account = null;
|
|
1711
1994
|
this.#set("signed-out");
|
|
1712
1995
|
this.#emit("bwalletx-signed-out");
|
|
@@ -1726,6 +2009,110 @@ function createClass() {
|
|
|
1726
2009
|
otherWallets() {
|
|
1727
2010
|
return this.#click("chooser");
|
|
1728
2011
|
}
|
|
2012
|
+
/** (0.6.0) "Switch": sign in with bWalletX (its sheet when here, else the chooser with Get bWalletX first). */
|
|
2013
|
+
switchToBwalletX() {
|
|
2014
|
+
return this.#click("bwalletx");
|
|
2015
|
+
}
|
|
2016
|
+
get #remember() {
|
|
2017
|
+
return this.#attr("remember") !== "off";
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* (0.7.0) "Use Metanet Desktop or another BRC-100 wallet" is offered: metanet on, or auto at lg/md when this
|
|
2021
|
+
* element signs in or follows a provider. Never on a phone or inside a frame.
|
|
2022
|
+
*/
|
|
2023
|
+
#metanetShown(size) {
|
|
2024
|
+
const a = this.#attr("metanet");
|
|
2025
|
+
if (a === "off" || !metanetOffered()) return false;
|
|
2026
|
+
if (a === "on") return true;
|
|
2027
|
+
return (size === "lg" || size === "md") && (this.#controlled || !!this.#attr("challenge-url"));
|
|
2028
|
+
}
|
|
2029
|
+
/** A Metanet sign-in is on screen: our own, or (driven) the provider's with Metanet remembered. */
|
|
2030
|
+
#metanetPending() {
|
|
2031
|
+
return this.#controlled ? this.#attr("wallet-type") === METANET_TYPE : this.#viaMetanet;
|
|
2032
|
+
}
|
|
2033
|
+
/** (0.7.0) The Metanet link: connect to Metanet Desktop on localhost:3321 (after this tap, never before). */
|
|
2034
|
+
useMetanet() {
|
|
2035
|
+
return this.#click("metanet");
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* (0.7.0) "Not you? Use another wallet": forget the remembered wallet here, and drop a Metanet sign-in in flight
|
|
2039
|
+
* (if its verify already set a session, logout-url ends it). Emits bwalletx-not-you (cancelable).
|
|
2040
|
+
*/
|
|
2041
|
+
notYou() {
|
|
2042
|
+
if (!this.#emit("bwalletx-not-you", { element: this }, true)) return;
|
|
2043
|
+
this.#run++;
|
|
2044
|
+
if (this.#remember) forgetWalletChoice();
|
|
2045
|
+
this.#signedType = null;
|
|
2046
|
+
if (this.#viaMetanet && !this.#controlled) {
|
|
2047
|
+
this.#viaMetanet = false;
|
|
2048
|
+
this.#busy = false;
|
|
2049
|
+
if (this.#phase === "pending") this.#phase = "signed-out";
|
|
2050
|
+
}
|
|
2051
|
+
this.#render();
|
|
2052
|
+
}
|
|
2053
|
+
/**
|
|
2054
|
+
* (0.7.0) Rule 4: back with Metanet remembered and no session, ONE silent attempt per page load (2 s, a known
|
|
2055
|
+
* identity key only), then the signature alone. Any stop or failure is silent: the normal button stays.
|
|
2056
|
+
*/
|
|
2057
|
+
async #silentMetanet() {
|
|
2058
|
+
if (this.#controlled || !this.#stateful || !this.#remember || this.#attr("require-token")) return;
|
|
2059
|
+
if (!this.#metanetShown(this.#attr("size") ?? "lg")) return;
|
|
2060
|
+
const challengeUrl = this.#attr("challenge-url");
|
|
2061
|
+
const verifyUrl = this.#attr("verify-url");
|
|
2062
|
+
if (!challengeUrl || !verifyUrl || this.#phase !== "signed-out" || this.#busy || silentAttemptUsed()) return;
|
|
2063
|
+
if (rememberedWalletChoice()?.type !== METANET_TYPE || !metanetOffered()) return;
|
|
2064
|
+
const run = ++this.#run;
|
|
2065
|
+
const live2 = () => run === this.#run;
|
|
2066
|
+
const started = Date.now();
|
|
2067
|
+
const r = await runSilentMetanet({
|
|
2068
|
+
challengeUrl,
|
|
2069
|
+
verifyUrl,
|
|
2070
|
+
post: (url, body) => postJson(url, body),
|
|
2071
|
+
isAbandoned: () => !live2(),
|
|
2072
|
+
logout: async () => {
|
|
2073
|
+
const out = this.#attr("logout-url");
|
|
2074
|
+
if (out) await fetch(out, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: "{}" }).catch(() => void 0);
|
|
2075
|
+
},
|
|
2076
|
+
onSignIn: () => {
|
|
2077
|
+
if (!live2() || this.#phase !== "signed-out" || this.#busy) return;
|
|
2078
|
+
this.#viaMetanet = true;
|
|
2079
|
+
this.#busy = true;
|
|
2080
|
+
this.#pendingLabel = PENDING_LABEL_OTHER;
|
|
2081
|
+
this.#set("pending");
|
|
2082
|
+
},
|
|
2083
|
+
onAttempt: (a) => this.#emit("bwalletx-attempt", a)
|
|
2084
|
+
});
|
|
2085
|
+
if (!live2()) return;
|
|
2086
|
+
this.#viaMetanet = false;
|
|
2087
|
+
this.#busy = false;
|
|
2088
|
+
if (!r) {
|
|
2089
|
+
if (this.#phase === "pending") this.#set("signed-out");
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
this.#signedType = r.walletType;
|
|
2093
|
+
this.#signedIn({ identityKey: r.identityKey, handle: r.handle, paymail: r.paymail });
|
|
2094
|
+
this.#emit("bwalletx-signed-in", { identityKey: r.identityKey, method: "wallet", nonce: null, signature: null, response: { handle: r.handle, paymail: r.paymail }, silent: true, ms: Date.now() - started });
|
|
2095
|
+
}
|
|
2096
|
+
/** The wallet type the Switch note judges: the provider's, else what signed in here, else the remembered one. */
|
|
2097
|
+
#walletType() {
|
|
2098
|
+
if (this.#controlled) return this.#attr("wallet-type");
|
|
2099
|
+
return this.#signedType ?? (this.#remember ? rememberedWalletChoice()?.type ?? null : null);
|
|
2100
|
+
}
|
|
2101
|
+
#switchNoteShown(size) {
|
|
2102
|
+
const a = this.#attr("switch-note");
|
|
2103
|
+
if (a === "off" || !tapGoesElsewhere(this.#walletType())) return false;
|
|
2104
|
+
return a === "on" || size === "lg" || size === "md";
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Signed in: note the wallet type, and remember it for this origin when the user picked it (0.6.0), plus the
|
|
2108
|
+
* identity key as known here (0.7.0, public keys only: Metanet's silent attempt goes on only for these). Pass a key
|
|
2109
|
+
* only after the site's verify route accepted it.
|
|
2110
|
+
*/
|
|
2111
|
+
#picked(option, reason, identityKey) {
|
|
2112
|
+
this.#signedType = walletTypeOf(option);
|
|
2113
|
+
if (this.#remember && isExplicitPick(option, reason)) rememberWalletChoice(option);
|
|
2114
|
+
if (this.#remember) rememberKnownIdentityKey(identityKey);
|
|
2115
|
+
}
|
|
1729
2116
|
#attempt(option, outcome, started) {
|
|
1730
2117
|
this.#emit("bwalletx-attempt", attemptFor(option, outcome, started));
|
|
1731
2118
|
}
|
|
@@ -1734,16 +2121,24 @@ function createClass() {
|
|
|
1734
2121
|
* when another wallet is here or the user asked; otherwise 'not-found'. null: the chooser was closed.
|
|
1735
2122
|
*/
|
|
1736
2123
|
async #choose(mode, ms, pairing) {
|
|
2124
|
+
if (mode === "metanet") return { option: metanetOption(), reason: "requested" };
|
|
2125
|
+
const r = mode === "signin" && this.#remember ? rememberedWalletChoice() : null;
|
|
2126
|
+
if (r && r.type !== METANET_TYPE && !(r.type === BWALLETX_TYPE && r.substrate !== "pairing")) {
|
|
2127
|
+
const m = matchRemembered(await discoverWallets({ ms, pairing: () => pairing }), r);
|
|
2128
|
+
if (m) return { option: m, reason: "remembered" };
|
|
2129
|
+
}
|
|
1737
2130
|
if (mode !== "chooser") {
|
|
1738
2131
|
const f = await discoverBwalletX(ms);
|
|
1739
2132
|
if (f) {
|
|
1740
2133
|
const substrate = f.rdns === RDNS_EXTENSION ? "extension" : f.rdns === RDNS_MOBILE ? "in-app" : "xdm";
|
|
1741
|
-
|
|
2134
|
+
const option = brc100Option(f.wallet, { id: f.rdns ?? "bwalletx-xdm", rdns: f.rdns ?? void 0, name: "bWalletX", isBwalletX: true, substrate });
|
|
2135
|
+
return { option, reason: "bwalletx" };
|
|
1742
2136
|
}
|
|
1743
2137
|
}
|
|
1744
2138
|
const list = await discoverWallets({ ms: 50, pairing: () => pairing });
|
|
1745
2139
|
if (mode !== "chooser" && !hasBwalletX(list) && !otherDetected(list).length) return "not-found";
|
|
1746
|
-
|
|
2140
|
+
const picked = await chooseWallet(list, { getUrl: this.#attr("get-url") ?? void 0 });
|
|
2141
|
+
return picked ? { option: picked, reason: "chooser" } : null;
|
|
1747
2142
|
}
|
|
1748
2143
|
async #click(mode = "signin") {
|
|
1749
2144
|
if (this.hasAttribute("disabled") || this.hasAttribute("loading") || this.#busy || this.state === "pending") return;
|
|
@@ -1767,9 +2162,12 @@ function createClass() {
|
|
|
1767
2162
|
const started = Date.now();
|
|
1768
2163
|
const pairing = pairingModal();
|
|
1769
2164
|
let option = null;
|
|
2165
|
+
const run = ++this.#run;
|
|
2166
|
+
const live2 = () => run === this.#run;
|
|
1770
2167
|
this.#notFound = false;
|
|
1771
2168
|
this.#busy = true;
|
|
1772
|
-
this.#
|
|
2169
|
+
this.#viaMetanet = mode === "metanet";
|
|
2170
|
+
this.#pendingLabel = mode === "metanet" ? PENDING_LABEL_OTHER : PENDING_LABEL;
|
|
1773
2171
|
if (own) this.#set("pending");
|
|
1774
2172
|
else this.#render();
|
|
1775
2173
|
try {
|
|
@@ -1788,12 +2186,14 @@ function createClass() {
|
|
|
1788
2186
|
this.#attempt(null, "cancelled", started);
|
|
1789
2187
|
return;
|
|
1790
2188
|
}
|
|
1791
|
-
option = picked;
|
|
2189
|
+
option = picked.option;
|
|
2190
|
+
const reason = picked.reason;
|
|
1792
2191
|
if (!option.isBwalletX) {
|
|
1793
2192
|
this.#pendingLabel = PENDING_LABEL_OTHER;
|
|
1794
2193
|
if (own) this.#render();
|
|
1795
2194
|
}
|
|
1796
2195
|
const cw = await option.connect({ identity: false });
|
|
2196
|
+
if (!live2()) return;
|
|
1797
2197
|
const wallet = cw.wallet;
|
|
1798
2198
|
if (!wallet) throw Object.assign(new Error("This wallet cannot sign in here"), { code: "NOT_SUPPORTED" });
|
|
1799
2199
|
const sub = substrateOf(option);
|
|
@@ -1811,6 +2211,7 @@ function createClass() {
|
|
|
1811
2211
|
challenge: (identityKey) => postJson(challengeUrl, { identityKey, require: hint }),
|
|
1812
2212
|
verify: verifyUrl2 ? (response2) => postAccess(verifyUrl2, response2) : void 0
|
|
1813
2213
|
});
|
|
2214
|
+
this.#picked(option, reason, verifyUrl2 ? a.response.identityKey : null);
|
|
1814
2215
|
this.#attempt(option, "signed-in", started);
|
|
1815
2216
|
this.#emit("bwalletx-access-granted", { identityKey: a.response.identityKey, response: a.verified });
|
|
1816
2217
|
} catch (e) {
|
|
@@ -1823,18 +2224,29 @@ function createClass() {
|
|
|
1823
2224
|
const verifyUrl = this.#attr("verify-url");
|
|
1824
2225
|
const { result: r, verified } = await signInAndVerify({
|
|
1825
2226
|
wallet,
|
|
2227
|
+
// Metanet hands over its key on connect (one request at a time), so its sign-in is the signature alone.
|
|
2228
|
+
identityKey: cw.identityKey ?? void 0,
|
|
2229
|
+
skipWaitForAuthentication: option.id === METANET_OPTION_ID,
|
|
2230
|
+
// its connect() just checked the lock
|
|
1826
2231
|
identityKeyHint: verifyUrl ? rememberedIdentity() : void 0,
|
|
1827
2232
|
challenge: (identityKey) => postJson(challengeUrl, identityKey ? { identityKey } : {}),
|
|
1828
2233
|
verify: async (res) => verifyUrl ? await postJson(verifyUrl, { identityKey: res.identityKey, nonce: res.nonce, signature: res.signature }) : {}
|
|
1829
2234
|
});
|
|
2235
|
+
if (!live2()) {
|
|
2236
|
+
const out = this.#attr("logout-url");
|
|
2237
|
+
if (verifyUrl && out) await fetch(out, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: "{}" }).catch(() => void 0);
|
|
2238
|
+
return;
|
|
2239
|
+
}
|
|
1830
2240
|
if (verifyUrl) rememberIdentity(r.identityKey);
|
|
1831
2241
|
const response = verifyUrl ? verified : null;
|
|
2242
|
+
this.#picked(option, reason, verifyUrl ? r.identityKey : null);
|
|
1832
2243
|
if (own) this.#signedIn({ identityKey: r.identityKey, handle: response?.handle ?? null, paymail: response?.paymail ?? null });
|
|
1833
2244
|
this.#attempt(option, "signed-in", started);
|
|
1834
2245
|
this.#emit("bwalletx-signed-in", { identityKey: r.identityKey, method, nonce: r.nonce, signature: r.signature, response });
|
|
1835
2246
|
} catch (error) {
|
|
1836
2247
|
const outcome = outcomeOf(error);
|
|
1837
2248
|
this.#attempt(option, outcome, started);
|
|
2249
|
+
if (!live2()) return;
|
|
1838
2250
|
const closedQr = outcome === "cancelled" && option?.kind === "pairing";
|
|
1839
2251
|
if (own) {
|
|
1840
2252
|
if (closedQr) this.#phase = prev;
|
|
@@ -1845,12 +2257,15 @@ function createClass() {
|
|
|
1845
2257
|
}
|
|
1846
2258
|
if (!closedQr) this.#emit("bwalletx-error", { error });
|
|
1847
2259
|
} finally {
|
|
1848
|
-
this.#busy = false;
|
|
1849
2260
|
try {
|
|
1850
2261
|
pairing.close();
|
|
1851
2262
|
} catch {
|
|
1852
2263
|
}
|
|
1853
|
-
|
|
2264
|
+
if (live2()) {
|
|
2265
|
+
this.#busy = false;
|
|
2266
|
+
this.#viaMetanet = false;
|
|
2267
|
+
this.#render();
|
|
2268
|
+
}
|
|
1854
2269
|
}
|
|
1855
2270
|
}
|
|
1856
2271
|
};
|
|
@@ -1867,6 +2282,16 @@ function defineBwalletxSignin(tag = SIGNIN_TAG) {
|
|
|
1867
2282
|
// src/wallet-button.ts
|
|
1868
2283
|
var BUTTON_TAG = "bwalletx-button";
|
|
1869
2284
|
var CONNECTED_KEY = "bwx:connected";
|
|
2285
|
+
function migrateConnectedKey() {
|
|
2286
|
+
const had = dropLegacyConnectedKey();
|
|
2287
|
+
if (had && !rememberedWalletChoice()) rememberBwalletX("extension");
|
|
2288
|
+
return had;
|
|
2289
|
+
}
|
|
2290
|
+
function rememberBwalletX(substrate) {
|
|
2291
|
+
rememberWalletChoice({ id: "bwalletx", name: "bWalletX", kind: substrate === "pairing" ? "pairing" : "brc100", isBwalletX: true, substrate, connect: async () => {
|
|
2292
|
+
throw new Error("unused");
|
|
2293
|
+
} });
|
|
2294
|
+
}
|
|
1870
2295
|
function dropLegacyConnectedKey() {
|
|
1871
2296
|
try {
|
|
1872
2297
|
const had = globalThis.localStorage?.getItem(CONNECTED_KEY) != null;
|
|
@@ -1936,7 +2361,7 @@ function createClass2() {
|
|
|
1936
2361
|
}
|
|
1937
2362
|
live.add(this);
|
|
1938
2363
|
this.#hideLegacy();
|
|
1939
|
-
|
|
2364
|
+
migrateConnectedKey();
|
|
1940
2365
|
const phone = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
|
1941
2366
|
this.#set(stateFor({ connected: false, found: null, phone }));
|
|
1942
2367
|
const ms = Number(this.getAttribute("discover-ms")) || 800;
|
|
@@ -2003,8 +2428,9 @@ function createClass2() {
|
|
|
2003
2428
|
const modal = pairingModal({ title: "Connect bWalletX" });
|
|
2004
2429
|
this.#set("connecting");
|
|
2005
2430
|
try {
|
|
2006
|
-
const { wallet } = await connectBwalletX({ pairing: modal });
|
|
2431
|
+
const { wallet, method } = await connectBwalletX({ pairing: modal });
|
|
2007
2432
|
const { publicKey } = await wallet.getPublicKey({ identityKey: true });
|
|
2433
|
+
rememberBwalletX(method === "pairing" ? "pairing" : method === "in-app" ? "in-app" : "extension");
|
|
2008
2434
|
this.#connected(publicKey);
|
|
2009
2435
|
} catch (e) {
|
|
2010
2436
|
if (e?.code === "NOT_FOUND") this.#set("none");
|
|
@@ -2031,6 +2457,7 @@ function createClass2() {
|
|
|
2031
2457
|
this.#key = null;
|
|
2032
2458
|
this.#paymail = null;
|
|
2033
2459
|
dropLegacyConnectedKey();
|
|
2460
|
+
if (rememberedWalletChoice()?.type === BWALLETX_TYPE) forgetWalletChoice();
|
|
2034
2461
|
this.#set("available");
|
|
2035
2462
|
this.#emit("bwalletx-disconnected");
|
|
2036
2463
|
}
|
|
@@ -2073,6 +2500,7 @@ defineBwalletxButton();
|
|
|
2073
2500
|
0 && (module.exports = {
|
|
2074
2501
|
BUTTON_TAG,
|
|
2075
2502
|
CONNECTED_KEY,
|
|
2503
|
+
CONTINUE_OTHER_LABEL,
|
|
2076
2504
|
GET_URL,
|
|
2077
2505
|
MENU_CSS,
|
|
2078
2506
|
PENDING_LABEL,
|
|
@@ -2080,6 +2508,7 @@ defineBwalletxButton();
|
|
|
2080
2508
|
PENDING_NOTE,
|
|
2081
2509
|
SIGNIN_CSS,
|
|
2082
2510
|
SIGNIN_TAG,
|
|
2511
|
+
SWITCH_NOTE,
|
|
2083
2512
|
WEB_WALLET_URL,
|
|
2084
2513
|
_resetBwalletxButtons,
|
|
2085
2514
|
buildAuthorizeUrl,
|
|
@@ -2089,6 +2518,7 @@ defineBwalletxButton();
|
|
|
2089
2518
|
getUrlFor,
|
|
2090
2519
|
handleLabel,
|
|
2091
2520
|
markSvg,
|
|
2521
|
+
migrateConnectedKey,
|
|
2092
2522
|
signinText,
|
|
2093
2523
|
stateFor
|
|
2094
2524
|
});
|