@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/index.cjs
CHANGED
|
@@ -40,13 +40,27 @@ __export(src_exports, {
|
|
|
40
40
|
BWALLETX_RDNS: () => BWALLETX_RDNS,
|
|
41
41
|
BWALLETX_TYPE: () => BWALLETX_TYPE,
|
|
42
42
|
CONNECTED_KEY: () => CONNECTED_KEY,
|
|
43
|
+
CONTINUE_OTHER_LABEL: () => CONTINUE_OTHER_LABEL,
|
|
43
44
|
DEFAULT_DISCOVER_MS: () => DEFAULT_DISCOVER_MS,
|
|
44
45
|
DESKTOP_OPTION_ID: () => DESKTOP_OPTION_ID,
|
|
45
46
|
GET_URL: () => GET_URL,
|
|
46
47
|
IDENTITY_HINT_KEY: () => IDENTITY_HINT_KEY,
|
|
48
|
+
KNOWN_KEYS_KEY: () => KNOWN_KEYS_KEY,
|
|
47
49
|
LOGIN_PROTOCOL: () => LOGIN_PROTOCOL,
|
|
48
50
|
LOGO_URL: () => LOGO_URL,
|
|
49
51
|
MENU_CSS: () => MENU_CSS,
|
|
52
|
+
METANET_ANSWER_MS: () => METANET_ANSWER_MS,
|
|
53
|
+
METANET_JSON_API: () => METANET_JSON_API,
|
|
54
|
+
METANET_LABEL: () => METANET_LABEL,
|
|
55
|
+
METANET_NOTE: () => METANET_NOTE,
|
|
56
|
+
METANET_OPTION_ID: () => METANET_OPTION_ID,
|
|
57
|
+
METANET_REMEMBERED_NOTE: () => METANET_REMEMBERED_NOTE,
|
|
58
|
+
METANET_SILENT_MS: () => METANET_SILENT_MS,
|
|
59
|
+
METANET_TYPE: () => METANET_TYPE,
|
|
60
|
+
METANET_UNREACHABLE: () => METANET_UNREACHABLE,
|
|
61
|
+
MetanetUnreachableError: () => MetanetUnreachableError,
|
|
62
|
+
NOT_YOU_LABEL: () => NOT_YOU_LABEL,
|
|
63
|
+
NO_COINS_TEXT: () => NO_COINS_TEXT,
|
|
50
64
|
PAYMAIL_LOOKUP_URL: () => PAYMAIL_LOOKUP_URL,
|
|
51
65
|
PENDING_LABEL: () => PENDING_LABEL,
|
|
52
66
|
PENDING_LABEL_OTHER: () => PENDING_LABEL_OTHER,
|
|
@@ -57,11 +71,14 @@ __export(src_exports, {
|
|
|
57
71
|
RDNS_MOBILE: () => RDNS_MOBILE,
|
|
58
72
|
SIGNIN_CSS: () => SIGNIN_CSS,
|
|
59
73
|
SIGNIN_TAG: () => SIGNIN_TAG,
|
|
74
|
+
SWITCH_NOTE: () => SWITCH_NOTE,
|
|
75
|
+
WALLET_CHOICE_KEY: () => WALLET_CHOICE_KEY,
|
|
60
76
|
WALLET_THEME_META: () => WALLET_THEME_META,
|
|
61
77
|
WEB_WALLET_URL: () => WEB_WALLET_URL,
|
|
62
78
|
accessErrorMessage: () => accessErrorMessage,
|
|
63
79
|
accountLabel: () => accountLabel,
|
|
64
80
|
amountInSats: () => amountInSats,
|
|
81
|
+
attachRemembered: () => attachRemembered,
|
|
65
82
|
attemptFor: () => attemptFor,
|
|
66
83
|
autoChoice: () => autoChoice,
|
|
67
84
|
avatarUrlFor: () => avatarUrlFor,
|
|
@@ -73,8 +90,10 @@ __export(src_exports, {
|
|
|
73
90
|
challengeWantsIdentityKey: () => challengeWantsIdentityKey,
|
|
74
91
|
chooseWallet: () => chooseWallet,
|
|
75
92
|
chooserOrder: () => chooserOrder,
|
|
93
|
+
claimSilentAttempt: () => claimSilentAttempt,
|
|
76
94
|
clearPairing: () => clearPairing,
|
|
77
95
|
connectBwalletX: () => connectBwalletX,
|
|
96
|
+
connectMetanetDesktop: () => connectMetanetDesktop,
|
|
78
97
|
cwiIsAnnounced: () => cwiIsAnnounced,
|
|
79
98
|
decideWallet: () => decideWallet,
|
|
80
99
|
declareWalletTheme: () => declareWalletTheme,
|
|
@@ -85,19 +104,33 @@ __export(src_exports, {
|
|
|
85
104
|
discoverWallets: () => discoverWallets,
|
|
86
105
|
dropLegacyConnectedKey: () => dropLegacyConnectedKey,
|
|
87
106
|
findBwalletX: () => findBwalletX,
|
|
107
|
+
forgetWalletChoice: () => forgetWalletChoice,
|
|
88
108
|
getUrlFor: () => getUrlFor,
|
|
89
109
|
handcashAdapter: () => handcashAdapter,
|
|
90
110
|
handleLabel: () => handleLabel,
|
|
91
111
|
hasBwalletX: () => hasBwalletX,
|
|
112
|
+
inAnyFrame: () => inAnyFrame,
|
|
92
113
|
inOrder: () => inOrder,
|
|
114
|
+
isExplicitPick: () => isExplicitPick,
|
|
93
115
|
isFramed: () => isFramed,
|
|
94
116
|
isIdentityKeyHex: () => isIdentityKeyHex,
|
|
117
|
+
isInsufficientFunds: () => isInsufficientFunds,
|
|
118
|
+
isKnownIdentityKey: () => isKnownIdentityKey,
|
|
119
|
+
isLocalDesktop: () => isLocalDesktop,
|
|
120
|
+
isLocalDesktopType: () => isLocalDesktopType,
|
|
95
121
|
isPaymail: () => isPaymail,
|
|
96
122
|
isPresentBwalletX: () => isPresentBwalletX,
|
|
123
|
+
isRememberable: () => isRememberable,
|
|
97
124
|
isTouchDevice: () => isTouchDevice,
|
|
98
125
|
isUserRejection: () => isUserRejection,
|
|
126
|
+
knownIdentityKeys: () => knownIdentityKeys,
|
|
99
127
|
lookupPaymail: () => lookupPaymail,
|
|
100
128
|
markSvg: () => markSvg,
|
|
129
|
+
matchRemembered: () => matchRemembered,
|
|
130
|
+
metanetOffered: () => metanetOffered,
|
|
131
|
+
metanetOption: () => metanetOption,
|
|
132
|
+
migrateConnectedKey: () => migrateConnectedKey,
|
|
133
|
+
needsBwalletX: () => needsBwalletX,
|
|
101
134
|
otherDetected: () => otherDetected,
|
|
102
135
|
outcomeOf: () => outcomeOf,
|
|
103
136
|
pairBwalletX: () => pairBwalletX,
|
|
@@ -105,30 +138,40 @@ __export(src_exports, {
|
|
|
105
138
|
pairingOption: () => pairingOption,
|
|
106
139
|
pay: () => pay,
|
|
107
140
|
pickWallet: () => pickWallet,
|
|
141
|
+
planAutoConnect: () => planAutoConnect,
|
|
108
142
|
prewarmBwalletX: () => prewarmBwalletX,
|
|
109
143
|
protocolMediaUrl: () => protocolMediaUrl,
|
|
110
144
|
proveHoldings: () => proveHoldings,
|
|
111
145
|
qrSvg: () => qrSvg,
|
|
112
146
|
rememberIdentity: () => rememberIdentity,
|
|
147
|
+
rememberKnownIdentityKey: () => rememberKnownIdentityKey,
|
|
148
|
+
rememberWalletChoice: () => rememberWalletChoice,
|
|
113
149
|
rememberedIdentity: () => rememberedIdentity,
|
|
150
|
+
rememberedWalletChoice: () => rememberedWalletChoice,
|
|
114
151
|
renderButton: () => renderButton,
|
|
115
152
|
reportAttempt: () => reportAttempt,
|
|
116
153
|
requestAccess: () => requestAccess,
|
|
154
|
+
resetSilentAttempt: () => resetSilentAttempt,
|
|
117
155
|
resolvePaymailP2P: () => resolvePaymailP2P,
|
|
118
156
|
restorePairing: () => restorePairing,
|
|
119
157
|
runConnect: () => runConnect,
|
|
158
|
+
runSilentMetanet: () => runSilentMetanet,
|
|
120
159
|
shortKey: () => shortKey,
|
|
121
160
|
signInAndVerify: () => signInAndVerify,
|
|
122
161
|
signInErrorText: () => signInErrorText,
|
|
123
162
|
signInWithBwalletX: () => signInWithBwalletX,
|
|
124
163
|
signinText: () => signinText,
|
|
164
|
+
silentAttemptUsed: () => silentAttemptUsed,
|
|
125
165
|
stateFor: () => stateFor,
|
|
126
166
|
substrateOf: () => substrateOf,
|
|
167
|
+
tapGoesElsewhere: () => tapGoesElsewhere,
|
|
127
168
|
throwIfAccessError: () => throwIfAccessError,
|
|
128
169
|
tip: () => tip,
|
|
129
170
|
toAccessError: () => toAccessError,
|
|
171
|
+
trySilentMetanet: () => trySilentMetanet,
|
|
130
172
|
waitIfLocked: () => waitIfLocked,
|
|
131
|
-
walletTypeOf: () => walletTypeOf
|
|
173
|
+
walletTypeOf: () => walletTypeOf,
|
|
174
|
+
warnIfNoLogout: () => warnIfNoLogout
|
|
132
175
|
});
|
|
133
176
|
module.exports = __toCommonJS(src_exports);
|
|
134
177
|
|
|
@@ -143,7 +186,7 @@ function discoverBwalletX(ms = DEFAULT_DISCOVER_MS) {
|
|
|
143
186
|
return new Promise((resolve) => {
|
|
144
187
|
let done = false;
|
|
145
188
|
let timer;
|
|
146
|
-
const
|
|
189
|
+
const finish2 = (r) => {
|
|
147
190
|
if (done) return;
|
|
148
191
|
done = true;
|
|
149
192
|
if (timer !== void 0) clearTimeout(timer);
|
|
@@ -154,19 +197,19 @@ function discoverBwalletX(ms = DEFAULT_DISCOVER_MS) {
|
|
|
154
197
|
const d = e.detail;
|
|
155
198
|
const rdns = d?.info?.rdns ?? "";
|
|
156
199
|
if (d?.wallet && BWALLETX_RDNS.includes(rdns)) {
|
|
157
|
-
|
|
200
|
+
finish2({ wallet: d.wallet, method: rdns === RDNS_EXTENSION ? "extension" : "in-app", rdns });
|
|
158
201
|
}
|
|
159
202
|
};
|
|
160
203
|
window.addEventListener("brc100:announceWallet", onAnnounce);
|
|
161
204
|
window.dispatchEvent(new Event("brc100:requestWallet"));
|
|
162
205
|
if (done) return;
|
|
163
206
|
if (isFramed()) {
|
|
164
|
-
void probeXdm(Math.max(ms, 1200)).then((w) =>
|
|
207
|
+
void probeXdm(Math.max(ms, 1200)).then((w) => finish2(w ? { wallet: w, method: "in-app", rdns: null } : null));
|
|
165
208
|
return;
|
|
166
209
|
}
|
|
167
210
|
timer = setTimeout(() => {
|
|
168
211
|
timer = void 0;
|
|
169
|
-
|
|
212
|
+
finish2(null);
|
|
170
213
|
}, ms);
|
|
171
214
|
});
|
|
172
215
|
}
|
|
@@ -903,15 +946,20 @@ function desktopOption(ms = 1500) {
|
|
|
903
946
|
return opt;
|
|
904
947
|
}
|
|
905
948
|
function autoChoice(list, force = false) {
|
|
906
|
-
const
|
|
907
|
-
const
|
|
908
|
-
|
|
949
|
+
const auto = list.filter((w) => !isLocalDesktop(w));
|
|
950
|
+
const detected = auto.filter((w) => w.kind === "brc100");
|
|
951
|
+
const others = auto.filter((w) => w.kind === "handcash" || w.kind === "custom");
|
|
952
|
+
const hasLocal = auto.length !== list.length;
|
|
953
|
+
if (force) return detected[0] ?? auto.find((w) => w.kind !== "pairing") ?? auto[0] ?? null;
|
|
909
954
|
if (!list.length) return null;
|
|
910
|
-
if (list.length === 1) return list[0];
|
|
911
|
-
if (detected.length === 1 && others.length === 0) return detected[0];
|
|
912
|
-
if (detected.length === 0 && others.length === 0) return
|
|
955
|
+
if (list.length === 1) return hasLocal ? void 0 : list[0];
|
|
956
|
+
if (detected.length === 1 && others.length === 0 && !hasLocal) return detected[0];
|
|
957
|
+
if (detected.length === 0 && others.length === 0) return auto[0] ?? void 0;
|
|
913
958
|
return void 0;
|
|
914
959
|
}
|
|
960
|
+
function isLocalDesktop(w) {
|
|
961
|
+
return w.substrate === "json-api" || w.substrate === "cicada" || w.id === "localhost:3321" || w.id === "localhost:3301" || w.id === DESKTOP_OPTION_ID;
|
|
962
|
+
}
|
|
915
963
|
|
|
916
964
|
// src/account.ts
|
|
917
965
|
var PAYMAIL_LOOKUP_URL = "https://pay.bwallet.space/api/paymail/lookup?key=";
|
|
@@ -947,6 +995,7 @@ function isUserRejection(e) {
|
|
|
947
995
|
}
|
|
948
996
|
function signInErrorText(e) {
|
|
949
997
|
if (isUserRejection(e)) return "Sign-in cancelled";
|
|
998
|
+
if (e?.name === "MetanetUnreachableError") return e.message;
|
|
950
999
|
const code = e?.code;
|
|
951
1000
|
if (code === "NOT_FOUND") return "bWalletX was not found in this browser";
|
|
952
1001
|
const msg = e instanceof Error ? e.message : String(e ?? "");
|
|
@@ -980,6 +1029,175 @@ function rememberIdentity(key) {
|
|
|
980
1029
|
}
|
|
981
1030
|
}
|
|
982
1031
|
|
|
1032
|
+
// src/metanet.ts
|
|
1033
|
+
var import_sdk5 = require("@bsv/sdk");
|
|
1034
|
+
|
|
1035
|
+
// src/device.ts
|
|
1036
|
+
function isTouchDevice(env = globalThis) {
|
|
1037
|
+
const nav = env.navigator;
|
|
1038
|
+
if (!nav) return false;
|
|
1039
|
+
const ua = nav.userAgent ?? "";
|
|
1040
|
+
if (/Android|iPhone|iPad|iPod|Mobile|Silk|Kindle/i.test(ua)) return true;
|
|
1041
|
+
if (/Macintosh/.test(ua) && (nav.maxTouchPoints ?? 0) > 1) return true;
|
|
1042
|
+
try {
|
|
1043
|
+
return !!env.matchMedia?.("(pointer: coarse)").matches && !env.matchMedia?.("(hover: hover)").matches;
|
|
1044
|
+
} catch {
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// src/metanet.ts
|
|
1050
|
+
var METANET_TYPE = "metanet";
|
|
1051
|
+
var METANET_OPTION_ID = METANET_TYPE;
|
|
1052
|
+
var METANET_JSON_API = "http://localhost:3321";
|
|
1053
|
+
var METANET_LABEL = "Use Metanet Desktop or another BRC-100 wallet";
|
|
1054
|
+
var METANET_NOTE = "Metanet Desktop must be running on this computer";
|
|
1055
|
+
var METANET_REMEMBERED_NOTE = "Signs in with Metanet Desktop on this computer.";
|
|
1056
|
+
var NOT_YOU_LABEL = "Not you? Use another wallet";
|
|
1057
|
+
var METANET_UNREACHABLE = "We couldn\u2019t reach Metanet Desktop. Open it, then try again.";
|
|
1058
|
+
var METANET_ANSWER_MS = 4e3;
|
|
1059
|
+
var METANET_SILENT_MS = 2e3;
|
|
1060
|
+
var MetanetUnreachableError = class extends Error {
|
|
1061
|
+
code = "NOT_FOUND";
|
|
1062
|
+
constructor() {
|
|
1063
|
+
super(METANET_UNREACHABLE);
|
|
1064
|
+
this.name = "MetanetUnreachableError";
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
function inAnyFrame(win = typeof window === "undefined" ? void 0 : window) {
|
|
1068
|
+
if (!win) return false;
|
|
1069
|
+
try {
|
|
1070
|
+
return win.top !== win.self;
|
|
1071
|
+
} catch {
|
|
1072
|
+
return true;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
function metanetOffered(env = {}) {
|
|
1076
|
+
const win = env.window ?? (typeof window === "undefined" ? void 0 : window);
|
|
1077
|
+
if (!win) return false;
|
|
1078
|
+
const nav = env.navigator ?? (typeof navigator === "undefined" ? void 0 : navigator);
|
|
1079
|
+
const mm = env.matchMedia ?? (typeof matchMedia === "undefined" ? void 0 : (q) => matchMedia(q));
|
|
1080
|
+
return !isTouchDevice({ navigator: nav, matchMedia: mm }) && !inAnyFrame(win);
|
|
1081
|
+
}
|
|
1082
|
+
function client(o) {
|
|
1083
|
+
if (o.make) return o.make();
|
|
1084
|
+
const originator = o.originator ?? (typeof location !== "undefined" && location.host ? location.host : void 0);
|
|
1085
|
+
return new import_sdk5.WalletClient("json-api", originator);
|
|
1086
|
+
}
|
|
1087
|
+
async function answers(wallet) {
|
|
1088
|
+
return Promise.resolve().then(() => wallet.getVersion({})).then((v) => typeof v?.version === "string", () => false);
|
|
1089
|
+
}
|
|
1090
|
+
async function connectMetanetDesktop(o = {}) {
|
|
1091
|
+
let wallet;
|
|
1092
|
+
try {
|
|
1093
|
+
wallet = client(o);
|
|
1094
|
+
} catch {
|
|
1095
|
+
throw new MetanetUnreachableError();
|
|
1096
|
+
}
|
|
1097
|
+
let timer;
|
|
1098
|
+
const ok = await Promise.race([answers(wallet), new Promise((r) => timer = setTimeout(() => r(false), o.timeoutMs ?? METANET_ANSWER_MS))]);
|
|
1099
|
+
clearTimeout(timer);
|
|
1100
|
+
if (!ok) throw new MetanetUnreachableError();
|
|
1101
|
+
return wallet;
|
|
1102
|
+
}
|
|
1103
|
+
function metanetOption(o = {}) {
|
|
1104
|
+
const opt = {
|
|
1105
|
+
id: METANET_OPTION_ID,
|
|
1106
|
+
name: METANET_LABEL,
|
|
1107
|
+
kind: "brc100",
|
|
1108
|
+
isBwalletX: false,
|
|
1109
|
+
substrate: "json-api",
|
|
1110
|
+
async connect() {
|
|
1111
|
+
const wallet = await connectMetanetDesktop(o);
|
|
1112
|
+
opt.wallet = wallet;
|
|
1113
|
+
await waitIfLocked(wallet);
|
|
1114
|
+
const { publicKey } = await wallet.getPublicKey({ identityKey: true });
|
|
1115
|
+
return { wallet, payer: wallet, identityKey: publicKey, optionId: opt.id, kind: "brc100" };
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
return opt;
|
|
1119
|
+
}
|
|
1120
|
+
var KNOWN_KEYS_KEY = "bwx:known-keys";
|
|
1121
|
+
var KEY_RE = /^0[23][0-9a-f]{64}$/;
|
|
1122
|
+
var MAX_KNOWN = 8;
|
|
1123
|
+
function knownIdentityKeys() {
|
|
1124
|
+
try {
|
|
1125
|
+
const v = JSON.parse(globalThis.localStorage?.getItem(KNOWN_KEYS_KEY) || "[]");
|
|
1126
|
+
return Array.isArray(v) ? v.filter((k) => typeof k === "string" && KEY_RE.test(k)) : [];
|
|
1127
|
+
} catch {
|
|
1128
|
+
return [];
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
function isKnownIdentityKey(key) {
|
|
1132
|
+
if (!key) return false;
|
|
1133
|
+
const k = key.toLowerCase();
|
|
1134
|
+
if (!KEY_RE.test(k)) return false;
|
|
1135
|
+
return knownIdentityKeys().includes(k) || rememberedIdentity()?.toLowerCase() === k;
|
|
1136
|
+
}
|
|
1137
|
+
function rememberKnownIdentityKey(key) {
|
|
1138
|
+
const k = (key ?? "").toLowerCase();
|
|
1139
|
+
if (!KEY_RE.test(k)) return;
|
|
1140
|
+
try {
|
|
1141
|
+
const list = [k, ...knownIdentityKeys().filter((x) => x !== k)].slice(0, MAX_KNOWN);
|
|
1142
|
+
globalThis.localStorage?.setItem(KNOWN_KEYS_KEY, JSON.stringify(list));
|
|
1143
|
+
} catch {
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
var triedThisLoad = false;
|
|
1147
|
+
function silentAttemptUsed() {
|
|
1148
|
+
return triedThisLoad;
|
|
1149
|
+
}
|
|
1150
|
+
function claimSilentAttempt() {
|
|
1151
|
+
if (triedThisLoad) return false;
|
|
1152
|
+
triedThisLoad = true;
|
|
1153
|
+
return true;
|
|
1154
|
+
}
|
|
1155
|
+
function resetSilentAttempt() {
|
|
1156
|
+
triedThisLoad = false;
|
|
1157
|
+
}
|
|
1158
|
+
async function trySilentMetanet(o = {}) {
|
|
1159
|
+
if (!metanetOffered()) return { ok: false, stop: "not-here" };
|
|
1160
|
+
if (!claimSilentAttempt()) return { ok: false, stop: "used" };
|
|
1161
|
+
const run = async () => {
|
|
1162
|
+
let wallet;
|
|
1163
|
+
try {
|
|
1164
|
+
wallet = client(o);
|
|
1165
|
+
} catch {
|
|
1166
|
+
return { ok: false, stop: "unreachable" };
|
|
1167
|
+
}
|
|
1168
|
+
if (!await answers(wallet)) return { ok: false, stop: "unreachable" };
|
|
1169
|
+
const unlocked = await Promise.resolve().then(() => wallet.isAuthenticated({})).then((r2) => r2?.authenticated === true, () => false);
|
|
1170
|
+
if (!unlocked) return { ok: false, stop: "locked" };
|
|
1171
|
+
const identityKey = await Promise.resolve().then(() => wallet.getPublicKey({ identityKey: true })).then((r2) => r2.publicKey, () => null);
|
|
1172
|
+
if (!identityKey) return { ok: false, stop: "unreachable" };
|
|
1173
|
+
if (!isKnownIdentityKey(identityKey)) return { ok: false, stop: "unknown-key" };
|
|
1174
|
+
return { ok: true, wallet, identityKey };
|
|
1175
|
+
};
|
|
1176
|
+
let timer;
|
|
1177
|
+
const r = await Promise.race([
|
|
1178
|
+
run().catch(() => ({ ok: false, stop: "unreachable" })),
|
|
1179
|
+
new Promise((res) => timer = setTimeout(() => res({ ok: false, stop: "slow" }), o.windowMs ?? METANET_SILENT_MS))
|
|
1180
|
+
]);
|
|
1181
|
+
clearTimeout(timer);
|
|
1182
|
+
return r;
|
|
1183
|
+
}
|
|
1184
|
+
var warnedLogout = false;
|
|
1185
|
+
var isProduction = () => {
|
|
1186
|
+
try {
|
|
1187
|
+
return globalThis.process?.env?.NODE_ENV === "production";
|
|
1188
|
+
} catch {
|
|
1189
|
+
return false;
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
function warnIfNoLogout(o) {
|
|
1193
|
+
if (!o.verifyUrl || o.logoutUrl || warnedLogout || isProduction() || typeof console === "undefined") return false;
|
|
1194
|
+
warnedLogout = true;
|
|
1195
|
+
console.warn(
|
|
1196
|
+
`@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.`
|
|
1197
|
+
);
|
|
1198
|
+
return true;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
983
1201
|
// src/choice.ts
|
|
984
1202
|
var BWALLETX_TYPE = "bwalletx";
|
|
985
1203
|
function substrateOf(o) {
|
|
@@ -1021,6 +1239,10 @@ function decideWallet(list, o = {}) {
|
|
|
1021
1239
|
const a = autoChoice([...list], o.picker === false);
|
|
1022
1240
|
return a === void 0 ? { option: null, reason: "chooser" } : a === null ? { option: null, reason: "none" } : { option: a, reason: "legacy" };
|
|
1023
1241
|
}
|
|
1242
|
+
if (o.remembered && !o.preferBwalletX) {
|
|
1243
|
+
const m = matchRemembered(list, o.remembered);
|
|
1244
|
+
if (m) return { option: m, reason: "remembered" };
|
|
1245
|
+
}
|
|
1024
1246
|
const b = list.find(isPresentBwalletX);
|
|
1025
1247
|
if (b) return { option: b, reason: "bwalletx" };
|
|
1026
1248
|
if (o.picker === false) {
|
|
@@ -1051,6 +1273,74 @@ function reportAttempt(hook, a) {
|
|
|
1051
1273
|
} catch {
|
|
1052
1274
|
}
|
|
1053
1275
|
}
|
|
1276
|
+
var WALLET_CHOICE_KEY = "bwx:wallet";
|
|
1277
|
+
var SUBSTRATES = ["extension", "in-app", "xdm", "injected", "window.CWI", "json-api", "cicada", "pairing", "adapter"];
|
|
1278
|
+
var TYPE_RE = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
1279
|
+
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;
|
|
1280
|
+
function isRememberable(o) {
|
|
1281
|
+
const t = walletTypeOf(o);
|
|
1282
|
+
return substrateOf(o) !== "window.CWI" && TYPE_RE.test(t) && !KEY_LIKE.test(t);
|
|
1283
|
+
}
|
|
1284
|
+
function isExplicitPick(o, reason) {
|
|
1285
|
+
return o.isBwalletX || reason === "chooser" || reason === "requested" || reason === "remembered";
|
|
1286
|
+
}
|
|
1287
|
+
function rememberWalletChoice(o) {
|
|
1288
|
+
try {
|
|
1289
|
+
if (!o) return void globalThis.localStorage?.removeItem(WALLET_CHOICE_KEY);
|
|
1290
|
+
if (!isRememberable(o)) return;
|
|
1291
|
+
const v = { type: walletTypeOf(o), substrate: substrateOf(o), v: 1 };
|
|
1292
|
+
globalThis.localStorage?.setItem(WALLET_CHOICE_KEY, JSON.stringify(v));
|
|
1293
|
+
} catch {
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
function rememberedWalletChoice() {
|
|
1297
|
+
try {
|
|
1298
|
+
const raw = globalThis.localStorage?.getItem(WALLET_CHOICE_KEY);
|
|
1299
|
+
if (!raw) return null;
|
|
1300
|
+
const j = JSON.parse(raw);
|
|
1301
|
+
if (typeof j.type !== "string" || !TYPE_RE.test(j.type) || KEY_LIKE.test(j.type)) return null;
|
|
1302
|
+
if (!SUBSTRATES.includes(j.substrate) || j.substrate === "window.CWI") return null;
|
|
1303
|
+
return { type: j.type, substrate: j.substrate };
|
|
1304
|
+
} catch {
|
|
1305
|
+
return null;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
function forgetWalletChoice() {
|
|
1309
|
+
rememberWalletChoice(null);
|
|
1310
|
+
}
|
|
1311
|
+
function needsBwalletX(type) {
|
|
1312
|
+
return !!type && type !== BWALLETX_TYPE;
|
|
1313
|
+
}
|
|
1314
|
+
function tapGoesElsewhere(type) {
|
|
1315
|
+
return needsBwalletX(type) && type !== METANET_TYPE && type !== DESKTOP_OPTION_ID;
|
|
1316
|
+
}
|
|
1317
|
+
function isLocalDesktopType(r) {
|
|
1318
|
+
return r.type === METANET_TYPE || r.type === DESKTOP_OPTION_ID || r.substrate === "json-api" || r.substrate === "cicada";
|
|
1319
|
+
}
|
|
1320
|
+
function matchRemembered(list, r) {
|
|
1321
|
+
if (isLocalDesktopType(r)) return null;
|
|
1322
|
+
if (r.type === BWALLETX_TYPE) {
|
|
1323
|
+
return (r.substrate === "pairing" ? list.find((o) => o.kind === "pairing") : list.find(isPresentBwalletX)) ?? null;
|
|
1324
|
+
}
|
|
1325
|
+
return list.find((o) => !o.isBwalletX && !isLocalDesktop(o) && isRememberable(o) && walletTypeOf(o) === r.type) ?? null;
|
|
1326
|
+
}
|
|
1327
|
+
var ATTACHABLE = ["extension", "in-app", "xdm", "injected"];
|
|
1328
|
+
function planAutoConnect(o) {
|
|
1329
|
+
const r = o.remembered;
|
|
1330
|
+
if (!r || o.enabled === false) return { action: "none", remembered: r, needsBwalletX: false };
|
|
1331
|
+
if (r.type === METANET_TYPE) {
|
|
1332
|
+
return { action: o.metanetHere ?? metanetOffered() ? "silent-metanet" : "none", remembered: r, needsBwalletX: false };
|
|
1333
|
+
}
|
|
1334
|
+
if (isLocalDesktopType(r)) return { action: "none", remembered: r, needsBwalletX: false };
|
|
1335
|
+
const action = o.signedIn && ATTACHABLE.includes(r.substrate) ? "attach" : "wait-for-tap";
|
|
1336
|
+
return { action, remembered: r, needsBwalletX: needsBwalletX(r.type) };
|
|
1337
|
+
}
|
|
1338
|
+
async function attachRemembered(r, o = {}) {
|
|
1339
|
+
if (!ATTACHABLE.includes(r.substrate)) return null;
|
|
1340
|
+
const list = await discoverWallets({ ms: o.ms });
|
|
1341
|
+
const m = matchRemembered(list, r);
|
|
1342
|
+
return m && m.kind === "brc100" && m.wallet && ATTACHABLE.includes(substrateOf(m)) ? m : null;
|
|
1343
|
+
}
|
|
1054
1344
|
|
|
1055
1345
|
// src/modal.ts
|
|
1056
1346
|
var import_qrcode_generator = __toESM(require("qrcode-generator"), 1);
|
|
@@ -1112,18 +1402,6 @@ function qrSvg(text) {
|
|
|
1112
1402
|
q.make();
|
|
1113
1403
|
return q.createSvgTag({ cellSize: 4, margin: 2, scalable: true });
|
|
1114
1404
|
}
|
|
1115
|
-
function isTouchDevice(env = globalThis) {
|
|
1116
|
-
const nav = env.navigator;
|
|
1117
|
-
if (!nav) return false;
|
|
1118
|
-
const ua = nav.userAgent ?? "";
|
|
1119
|
-
if (/Android|iPhone|iPad|iPod|Mobile|Silk|Kindle/i.test(ua)) return true;
|
|
1120
|
-
if (/Macintosh/.test(ua) && (nav.maxTouchPoints ?? 0) > 1) return true;
|
|
1121
|
-
try {
|
|
1122
|
-
return !!env.matchMedia?.("(pointer: coarse)").matches && !env.matchMedia?.("(hover: hover)").matches;
|
|
1123
|
-
} catch {
|
|
1124
|
-
return false;
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
1405
|
function pairingModal(o = {}) {
|
|
1128
1406
|
const ac = new AbortController();
|
|
1129
1407
|
let ui = null;
|
|
@@ -1284,10 +1562,11 @@ function chooseWallet(options, o = {}) {
|
|
|
1284
1562
|
row2.onclick = () => done(w);
|
|
1285
1563
|
list.append(row2);
|
|
1286
1564
|
}
|
|
1287
|
-
if (o.desktop !== false && !options.some((w) => w.substrate === "json-api" || w.substrate === "cicada")) {
|
|
1565
|
+
if (o.desktop !== false && metanetOffered() && !options.some((w) => w.substrate === "json-api" || w.substrate === "cicada")) {
|
|
1288
1566
|
if (!here && !sep) list.append(el("p", "bwx-sep", "Or use"));
|
|
1289
|
-
const d =
|
|
1290
|
-
const row2 = optionRow(d, false,
|
|
1567
|
+
const d = metanetOption();
|
|
1568
|
+
const row2 = optionRow(d, false, METANET_NOTE);
|
|
1569
|
+
row2.title = "Connects to Metanet Desktop on this computer, never to a browser extension.";
|
|
1291
1570
|
row2.onclick = () => done(d);
|
|
1292
1571
|
list.append(row2);
|
|
1293
1572
|
}
|
|
@@ -1305,7 +1584,7 @@ function chooseWallet(options, o = {}) {
|
|
|
1305
1584
|
}
|
|
1306
1585
|
|
|
1307
1586
|
// src/access-client.ts
|
|
1308
|
-
var
|
|
1587
|
+
var import_sdk6 = require("@bsv/sdk");
|
|
1309
1588
|
|
|
1310
1589
|
// src/access.ts
|
|
1311
1590
|
var ACCESS_VERSION = 1;
|
|
@@ -1491,7 +1770,7 @@ async function proveHoldings(wallet, challenge, tokenId, basket = "bsv21") {
|
|
|
1491
1770
|
const counterparty = ci.counterparty ?? "self";
|
|
1492
1771
|
for (const protocolID of ci.protocolID ? [ci.protocolID] : [ONESAT, LEGACY_ONESAT]) {
|
|
1493
1772
|
const { publicKey } = await wallet.getPublicKey({ protocolID, keyID: ci.keyID, counterparty });
|
|
1494
|
-
const h =
|
|
1773
|
+
const h = import_sdk6.Utils.toHex(import_sdk6.Hash.hash160(import_sdk6.PublicKey.fromString(publicKey).encode(true)));
|
|
1495
1774
|
if (want && h !== want) continue;
|
|
1496
1775
|
const existing = byKey.get(publicKey);
|
|
1497
1776
|
if (existing) {
|
|
@@ -1537,6 +1816,114 @@ async function requestAccess(o) {
|
|
|
1537
1816
|
}
|
|
1538
1817
|
}
|
|
1539
1818
|
|
|
1819
|
+
// src/connect-flow.ts
|
|
1820
|
+
async function runConnect(o) {
|
|
1821
|
+
const started = Date.now();
|
|
1822
|
+
const { list } = o;
|
|
1823
|
+
let choice = null;
|
|
1824
|
+
let reason = "none";
|
|
1825
|
+
try {
|
|
1826
|
+
if (o.metanet) {
|
|
1827
|
+
if (!metanetOffered()) throw Object.assign(new Error("Metanet Desktop runs on a computer, outside any frame."), { code: "NOT_SUPPORTED" });
|
|
1828
|
+
choice = metanetOption(typeof o.metanet === "object" ? o.metanet : {});
|
|
1829
|
+
reason = "requested";
|
|
1830
|
+
return await finish(o, choice, reason, started);
|
|
1831
|
+
}
|
|
1832
|
+
const d = decideWallet(list, {
|
|
1833
|
+
walletId: o.walletId,
|
|
1834
|
+
chooser: o.chooser,
|
|
1835
|
+
picker: o.picker,
|
|
1836
|
+
fastPath: o.fastPath,
|
|
1837
|
+
remembered: o.remembered,
|
|
1838
|
+
preferBwalletX: o.preferBwalletX
|
|
1839
|
+
});
|
|
1840
|
+
choice = d.option;
|
|
1841
|
+
reason = d.reason;
|
|
1842
|
+
if (!choice && d.reason === "chooser") choice = await o.pick(list);
|
|
1843
|
+
if (!choice) {
|
|
1844
|
+
if (d.reason === "none" || !list.length) throw Object.assign(new Error("No wallet found. Get bWalletX at bwalletx.com."), { code: "NOT_FOUND" });
|
|
1845
|
+
reportAttempt(o.onAttempt, attemptFor(null, "cancelled", started));
|
|
1846
|
+
return null;
|
|
1847
|
+
}
|
|
1848
|
+
return await finish(o, choice, reason, started);
|
|
1849
|
+
} catch (e) {
|
|
1850
|
+
reportAttempt(o.onAttempt, attemptFor(choice, outcomeOf(e), started));
|
|
1851
|
+
throw e;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
async function finish(o, choice, reason, started) {
|
|
1855
|
+
const signIn = !!(o.challengeUrl && o.verifyUrl) && choice.kind !== "handcash";
|
|
1856
|
+
const cw = await choice.connect(signIn ? { identity: false } : void 0);
|
|
1857
|
+
let handle = cw.handle ?? null;
|
|
1858
|
+
let paymail = cw.paymail ?? null;
|
|
1859
|
+
let identityKey = cw.identityKey;
|
|
1860
|
+
let signedIn = false;
|
|
1861
|
+
if (cw.wallet && signIn) {
|
|
1862
|
+
const { result, verified } = await signInAndVerify({
|
|
1863
|
+
wallet: cw.wallet,
|
|
1864
|
+
identityKey: cw.identityKey ?? void 0,
|
|
1865
|
+
// Metanet's connect() just checked the lock: don't ask again (rule 7, the minimum).
|
|
1866
|
+
skipWaitForAuthentication: choice.id === METANET_OPTION_ID,
|
|
1867
|
+
// The key that signed in here last time: a repeat sign-in is then the signature alone.
|
|
1868
|
+
identityKeyHint: rememberedIdentity(),
|
|
1869
|
+
challengeNeedsIdentityKey: o.challengeNeedsIdentityKey,
|
|
1870
|
+
challenge: (key) => o.post(o.challengeUrl, key ? { identityKey: key } : {}),
|
|
1871
|
+
verify: (r) => o.post(o.verifyUrl, { identityKey: r.identityKey, nonce: r.nonce, signature: r.signature })
|
|
1872
|
+
});
|
|
1873
|
+
rememberIdentity(result.identityKey);
|
|
1874
|
+
identityKey = result.identityKey;
|
|
1875
|
+
handle = verified.handle ?? handle;
|
|
1876
|
+
paymail = verified.paymail ?? paymail;
|
|
1877
|
+
signedIn = true;
|
|
1878
|
+
} else if (cw.wallet && !identityKey) {
|
|
1879
|
+
identityKey = (await cw.wallet.getPublicKey({ identityKey: true })).publicKey;
|
|
1880
|
+
}
|
|
1881
|
+
if (o.remember && isExplicitPick(choice, reason)) rememberWalletChoice(choice);
|
|
1882
|
+
if (o.remember && signedIn) rememberKnownIdentityKey(identityKey);
|
|
1883
|
+
reportAttempt(o.onAttempt, attemptFor(choice, signedIn ? "signed-in" : "connected", started));
|
|
1884
|
+
return { connected: cw, identityKey, handle, paymail, signedIn, walletType: walletTypeOf(choice), substrate: substrateOf(choice), reason };
|
|
1885
|
+
}
|
|
1886
|
+
async function runSilentMetanet(o = {}) {
|
|
1887
|
+
const s = await trySilentMetanet(o);
|
|
1888
|
+
if (!s.ok || o.isAbandoned?.()) return null;
|
|
1889
|
+
const started = Date.now();
|
|
1890
|
+
const opt = metanetOption(o);
|
|
1891
|
+
opt.wallet = s.wallet;
|
|
1892
|
+
const cw = { wallet: s.wallet, payer: s.wallet, identityKey: s.identityKey, optionId: METANET_OPTION_ID, kind: "brc100" };
|
|
1893
|
+
const done = (signedIn, handle = null, paymail = null) => {
|
|
1894
|
+
rememberWalletChoice(opt);
|
|
1895
|
+
if (signedIn) rememberKnownIdentityKey(s.identityKey);
|
|
1896
|
+
reportAttempt(o.onAttempt, attemptFor(opt, signedIn ? "signed-in" : "connected", started));
|
|
1897
|
+
return { connected: cw, identityKey: s.identityKey, handle, paymail, signedIn, walletType: walletTypeOf(opt), substrate: "json-api", reason: "remembered" };
|
|
1898
|
+
};
|
|
1899
|
+
if (o.sessionKey !== void 0 && o.sessionKey !== null) {
|
|
1900
|
+
return o.sessionKey.toLowerCase() === s.identityKey.toLowerCase() ? done(true) : null;
|
|
1901
|
+
}
|
|
1902
|
+
if (!o.challengeUrl || !o.verifyUrl || !o.post) return done(false);
|
|
1903
|
+
if (!isKnownIdentityKey(s.identityKey)) return null;
|
|
1904
|
+
try {
|
|
1905
|
+
o.onSignIn?.();
|
|
1906
|
+
const post = o.post;
|
|
1907
|
+
const { result, verified } = await signInAndVerify({
|
|
1908
|
+
wallet: s.wallet,
|
|
1909
|
+
identityKey: s.identityKey,
|
|
1910
|
+
skipWaitForAuthentication: true,
|
|
1911
|
+
// trySilentMetanet just saw it unlocked
|
|
1912
|
+
challenge: (key) => post(o.challengeUrl, key ? { identityKey: key } : {}),
|
|
1913
|
+
verify: (r) => post(o.verifyUrl, { identityKey: r.identityKey, nonce: r.nonce, signature: r.signature })
|
|
1914
|
+
});
|
|
1915
|
+
if (o.isAbandoned?.()) {
|
|
1916
|
+
await Promise.resolve(o.logout?.()).catch(() => void 0);
|
|
1917
|
+
return null;
|
|
1918
|
+
}
|
|
1919
|
+
rememberIdentity(result.identityKey);
|
|
1920
|
+
return done(true, verified.handle ?? null, verified.paymail ?? null);
|
|
1921
|
+
} catch (e) {
|
|
1922
|
+
reportAttempt(o.onAttempt, attemptFor(opt, outcomeOf(e), started));
|
|
1923
|
+
return null;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1540
1927
|
// src/element.ts
|
|
1541
1928
|
var SIGNIN_TAG = "bwalletx-signin";
|
|
1542
1929
|
var GET_URL = "https://bwalletx.com/get";
|
|
@@ -1602,6 +1989,11 @@ button[aria-busy=true]{cursor:wait}
|
|
|
1602
1989
|
/* "Other wallets" (0.5.6): one small link under the button */
|
|
1603
1990
|
.ow{margin:8px 0 0;width:0;min-width:100%;font-size:12px;line-height:1.4;color:#71717a;text-align:center}
|
|
1604
1991
|
:host([theme=dark]) .ow,:host-context(.dark) .ow{color:#a1a1aa}
|
|
1992
|
+
.sw{margin:8px 0 0;width:0;min-width:100%;font-size:12px;line-height:1.4;color:#a16207;text-align:center}
|
|
1993
|
+
:host([theme=dark]) .sw,:host-context(.dark) .sw{color:#fcd34d}
|
|
1994
|
+
.mn{margin:8px 0 0;width:0;min-width:100%;font-size:12px;line-height:1.4;color:#71717a;text-align:center}
|
|
1995
|
+
.mn small{display:block;font-size:11px;opacity:.85}
|
|
1996
|
+
:host([theme=dark]) .mn,:host-context(.dark) .mn{color:#a1a1aa}
|
|
1605
1997
|
.lnk{all:unset;cursor:pointer;color:inherit;font-weight:600;text-decoration:underline;text-underline-offset:2px}
|
|
1606
1998
|
.lnk:focus-visible{outline:2px solid #b45309;outline-offset:2px;border-radius:2px}
|
|
1607
1999
|
/* error: one line plus Retry */
|
|
@@ -1640,6 +2032,11 @@ var MENU_CSS = `
|
|
|
1640
2032
|
var PENDING_NOTE = "Sign in: shares only your public identity key, moves no money";
|
|
1641
2033
|
var PENDING_LABEL = "Approve in bWalletX\u2026";
|
|
1642
2034
|
var PENDING_LABEL_OTHER = "Approve in your wallet\u2026";
|
|
2035
|
+
var SWITCH_NOTE = "Some features need bWalletX";
|
|
2036
|
+
var CONTINUE_OTHER_LABEL = "Continue with your wallet";
|
|
2037
|
+
function walletGlyph(px) {
|
|
2038
|
+
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>`;
|
|
2039
|
+
}
|
|
1643
2040
|
function b64url2(bytes) {
|
|
1644
2041
|
let s = "";
|
|
1645
2042
|
for (const b of bytes) s += String.fromCharCode(b);
|
|
@@ -1679,7 +2076,7 @@ async function postAccess(url, body) {
|
|
|
1679
2076
|
const res = await fetch(url, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
1680
2077
|
return res.json().catch(() => ({ error: "bad_request", message: `HTTP ${res.status}` }));
|
|
1681
2078
|
}
|
|
1682
|
-
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"];
|
|
2079
|
+
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"];
|
|
1683
2080
|
var STATES = ["signed-out", "restoring", "pending", "signed-in", "error"];
|
|
1684
2081
|
function createClass() {
|
|
1685
2082
|
return class BwalletxSigninElement extends HTMLElement {
|
|
@@ -1694,14 +2091,21 @@ function createClass() {
|
|
|
1694
2091
|
#error = "";
|
|
1695
2092
|
#restored = false;
|
|
1696
2093
|
#pendingLabel = PENDING_LABEL;
|
|
2094
|
+
/** The wallet type signed in with here (0.6.0), for the Switch note. */
|
|
2095
|
+
#signedType = null;
|
|
1697
2096
|
#menu = null;
|
|
2097
|
+
/** (0.7.0) A Metanet sign-in (tapped or silent) is in flight. */
|
|
2098
|
+
#viaMetanet = false;
|
|
2099
|
+
/** (0.7.0) Bumped by "Not you?": a Metanet sign-in started before it lands nobody. */
|
|
2100
|
+
#run = 0;
|
|
1698
2101
|
constructor() {
|
|
1699
2102
|
super();
|
|
1700
2103
|
this.#root = this.attachShadow({ mode: "open" });
|
|
1701
2104
|
}
|
|
1702
2105
|
connectedCallback() {
|
|
2106
|
+
if (!this.#controlled && this.#stateful) warnIfNoLogout({ verifyUrl: this.#attr("verify-url"), logoutUrl: this.#attr("logout-url"), where: "<bwalletx-signin>" });
|
|
1703
2107
|
this.#render();
|
|
1704
|
-
void this.#restore();
|
|
2108
|
+
void this.#restore().then(() => this.#silentMetanet());
|
|
1705
2109
|
}
|
|
1706
2110
|
disconnectedCallback() {
|
|
1707
2111
|
this.#menu?.close(false);
|
|
@@ -1807,10 +2211,11 @@ function createClass() {
|
|
|
1807
2211
|
const restoring = state === "restoring";
|
|
1808
2212
|
const error = state === "error";
|
|
1809
2213
|
const pendingLabel = this.#controlled ? PENDING_LABEL : this.#pendingLabel;
|
|
1810
|
-
const
|
|
2214
|
+
const elsewhere = state === "signed-out" && tapGoesElsewhere(this.#walletType());
|
|
2215
|
+
const text = pending ? pendingLabel : error ? "Retry" : elsewhere ? CONTINUE_OTHER_LABEL : signinText(this.#attr("label") ?? "signin", brand);
|
|
1811
2216
|
const subAttr = this.#attr("subtitle");
|
|
1812
2217
|
const subDefault = brand === "bWalletX" && (size === "lg" || size === "md") ? "also works with bWallet" : null;
|
|
1813
|
-
const subtitle = pending || error ? null : subAttr === null || subAttr === "" || subAttr === "on" ? subAttr === "on" ? "also works with bWallet" : subDefault : subAttr === "off" ? null : subAttr;
|
|
2218
|
+
const subtitle = pending || error || elsewhere ? null : subAttr === null || subAttr === "" || subAttr === "on" ? subAttr === "on" ? "also works with bWallet" : subDefault : subAttr === "off" ? null : subAttr;
|
|
1814
2219
|
const loading = this.hasAttribute("loading") || this.#busy || pending || restoring;
|
|
1815
2220
|
const disabled = this.hasAttribute("disabled");
|
|
1816
2221
|
const markPx = size === "icon" ? 24 : size === "sm" ? 15 : size === "md" ? 17 : 20;
|
|
@@ -1819,18 +2224,26 @@ function createClass() {
|
|
|
1819
2224
|
const webUrl = this.#attr("web-url") ?? WEB_WALLET_URL;
|
|
1820
2225
|
const errText = error ? (this.#controlled ? this.#attr("error") : this.#error) || "Sign-in failed" : "";
|
|
1821
2226
|
const label = size === "icon" ? "" : `<span class="txt" part="text"><b part="label">${esc(text)}</b>${subtitle ? `<small part="subtitle">${esc(subtitle)}</small>` : ""}</span>`;
|
|
1822
|
-
const mark = loading ? '<span class="spin" aria-hidden="true"></span>' : markSvg(markPx);
|
|
2227
|
+
const mark = loading ? '<span class="spin" aria-hidden="true"></span>' : elsewhere ? walletGlyph(markPx - 4) : markSvg(markPx);
|
|
1823
2228
|
const otherOff = this.#attr("other-wallets") === "off";
|
|
1824
2229
|
const otherLink = '<button type="button" class="lnk" data-act="other" part="other-wallets-link">Other wallets</button>';
|
|
1825
2230
|
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>` : "";
|
|
1826
2231
|
const ow = !this.#notFound && !pending && !restoring && this.#otherWalletsShown(size) ? `<p class="ow" part="other-wallets">${otherLink}</p>` : "";
|
|
2232
|
+
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>` : "";
|
|
1827
2233
|
const err = error ? `<p class="err" part="error" role="alert">${esc(errText)}</p>` : "";
|
|
2234
|
+
const mnHere = this.#metanetShown(size);
|
|
2235
|
+
const mnRemembered = mnHere && (pending ? this.#metanetPending() : !restoring && this.#walletType() === METANET_TYPE);
|
|
2236
|
+
const notYou = `<button type="button" class="lnk" data-act="not-you" part="not-you-link">${esc(NOT_YOU_LABEL)}</button>`;
|
|
2237
|
+
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>`;
|
|
1828
2238
|
const compact = size === "sm" || size === "icon";
|
|
1829
2239
|
const note = pending && !compact ? `<p class="note" part="note">${esc(PENDING_NOTE)}</p>` : "";
|
|
1830
2240
|
const status = pending ? `<span class="sr" role="status">${esc(pendingLabel)}</span>` : loading ? '<span class="sr" role="status">Connecting to bWalletX\u2026</span>' : "";
|
|
1831
|
-
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}`;
|
|
2241
|
+
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}`;
|
|
1832
2242
|
this.button.addEventListener("click", () => void this.#click());
|
|
1833
2243
|
for (const l of Array.from(this.#root.querySelectorAll("[data-act=other]"))) l.addEventListener("click", () => void this.otherWallets());
|
|
2244
|
+
this.#root.querySelector("[data-act=switch]")?.addEventListener("click", () => void this.switchToBwalletX());
|
|
2245
|
+
this.#root.querySelector("[data-act=metanet]")?.addEventListener("click", () => void this.useMetanet());
|
|
2246
|
+
this.#root.querySelector("[data-act=not-you]")?.addEventListener("click", () => this.notYou());
|
|
1834
2247
|
bindPrewarm(this.button);
|
|
1835
2248
|
if (pending && compact) this.#showTip(PENDING_NOTE);
|
|
1836
2249
|
else this.#hideTip();
|
|
@@ -1898,7 +2311,20 @@ function createClass() {
|
|
|
1898
2311
|
close(false);
|
|
1899
2312
|
void this.signOut();
|
|
1900
2313
|
});
|
|
1901
|
-
|
|
2314
|
+
const items = [copy, out];
|
|
2315
|
+
menu.append(who);
|
|
2316
|
+
if (needsBwalletX(this.#walletType())) {
|
|
2317
|
+
const sw = item("Switch to bWalletX", () => {
|
|
2318
|
+
close(false);
|
|
2319
|
+
void this.switchToBwalletX();
|
|
2320
|
+
});
|
|
2321
|
+
const line = document.createElement("div");
|
|
2322
|
+
line.className = "who";
|
|
2323
|
+
line.textContent = SWITCH_NOTE;
|
|
2324
|
+
menu.append(line, sw);
|
|
2325
|
+
items.unshift(sw);
|
|
2326
|
+
}
|
|
2327
|
+
menu.append(copy, out);
|
|
1902
2328
|
root.append(style, menu);
|
|
1903
2329
|
document.body.appendChild(host);
|
|
1904
2330
|
const place = () => {
|
|
@@ -1914,7 +2340,6 @@ function createClass() {
|
|
|
1914
2340
|
menu.style.top = `${below + h > vh && r.top - 8 - h > 0 ? r.top - 8 - h : below}px`;
|
|
1915
2341
|
};
|
|
1916
2342
|
place();
|
|
1917
|
-
const items = [copy, out];
|
|
1918
2343
|
const focusAt = (i) => items[(i + items.length) % items.length].focus();
|
|
1919
2344
|
const onKey = (e) => {
|
|
1920
2345
|
const i = items.indexOf(root.activeElement);
|
|
@@ -1969,6 +2394,8 @@ function createClass() {
|
|
|
1969
2394
|
if (this.#controlled) return;
|
|
1970
2395
|
const url = this.#attr("logout-url");
|
|
1971
2396
|
if (url) await fetch(url, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: "{}" }).catch(() => void 0);
|
|
2397
|
+
if (this.#remember) forgetWalletChoice();
|
|
2398
|
+
this.#signedType = null;
|
|
1972
2399
|
this.#account = null;
|
|
1973
2400
|
this.#set("signed-out");
|
|
1974
2401
|
this.#emit("bwalletx-signed-out");
|
|
@@ -1988,6 +2415,110 @@ function createClass() {
|
|
|
1988
2415
|
otherWallets() {
|
|
1989
2416
|
return this.#click("chooser");
|
|
1990
2417
|
}
|
|
2418
|
+
/** (0.6.0) "Switch": sign in with bWalletX (its sheet when here, else the chooser with Get bWalletX first). */
|
|
2419
|
+
switchToBwalletX() {
|
|
2420
|
+
return this.#click("bwalletx");
|
|
2421
|
+
}
|
|
2422
|
+
get #remember() {
|
|
2423
|
+
return this.#attr("remember") !== "off";
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* (0.7.0) "Use Metanet Desktop or another BRC-100 wallet" is offered: metanet on, or auto at lg/md when this
|
|
2427
|
+
* element signs in or follows a provider. Never on a phone or inside a frame.
|
|
2428
|
+
*/
|
|
2429
|
+
#metanetShown(size) {
|
|
2430
|
+
const a = this.#attr("metanet");
|
|
2431
|
+
if (a === "off" || !metanetOffered()) return false;
|
|
2432
|
+
if (a === "on") return true;
|
|
2433
|
+
return (size === "lg" || size === "md") && (this.#controlled || !!this.#attr("challenge-url"));
|
|
2434
|
+
}
|
|
2435
|
+
/** A Metanet sign-in is on screen: our own, or (driven) the provider's with Metanet remembered. */
|
|
2436
|
+
#metanetPending() {
|
|
2437
|
+
return this.#controlled ? this.#attr("wallet-type") === METANET_TYPE : this.#viaMetanet;
|
|
2438
|
+
}
|
|
2439
|
+
/** (0.7.0) The Metanet link: connect to Metanet Desktop on localhost:3321 (after this tap, never before). */
|
|
2440
|
+
useMetanet() {
|
|
2441
|
+
return this.#click("metanet");
|
|
2442
|
+
}
|
|
2443
|
+
/**
|
|
2444
|
+
* (0.7.0) "Not you? Use another wallet": forget the remembered wallet here, and drop a Metanet sign-in in flight
|
|
2445
|
+
* (if its verify already set a session, logout-url ends it). Emits bwalletx-not-you (cancelable).
|
|
2446
|
+
*/
|
|
2447
|
+
notYou() {
|
|
2448
|
+
if (!this.#emit("bwalletx-not-you", { element: this }, true)) return;
|
|
2449
|
+
this.#run++;
|
|
2450
|
+
if (this.#remember) forgetWalletChoice();
|
|
2451
|
+
this.#signedType = null;
|
|
2452
|
+
if (this.#viaMetanet && !this.#controlled) {
|
|
2453
|
+
this.#viaMetanet = false;
|
|
2454
|
+
this.#busy = false;
|
|
2455
|
+
if (this.#phase === "pending") this.#phase = "signed-out";
|
|
2456
|
+
}
|
|
2457
|
+
this.#render();
|
|
2458
|
+
}
|
|
2459
|
+
/**
|
|
2460
|
+
* (0.7.0) Rule 4: back with Metanet remembered and no session, ONE silent attempt per page load (2 s, a known
|
|
2461
|
+
* identity key only), then the signature alone. Any stop or failure is silent: the normal button stays.
|
|
2462
|
+
*/
|
|
2463
|
+
async #silentMetanet() {
|
|
2464
|
+
if (this.#controlled || !this.#stateful || !this.#remember || this.#attr("require-token")) return;
|
|
2465
|
+
if (!this.#metanetShown(this.#attr("size") ?? "lg")) return;
|
|
2466
|
+
const challengeUrl = this.#attr("challenge-url");
|
|
2467
|
+
const verifyUrl = this.#attr("verify-url");
|
|
2468
|
+
if (!challengeUrl || !verifyUrl || this.#phase !== "signed-out" || this.#busy || silentAttemptUsed()) return;
|
|
2469
|
+
if (rememberedWalletChoice()?.type !== METANET_TYPE || !metanetOffered()) return;
|
|
2470
|
+
const run = ++this.#run;
|
|
2471
|
+
const live2 = () => run === this.#run;
|
|
2472
|
+
const started = Date.now();
|
|
2473
|
+
const r = await runSilentMetanet({
|
|
2474
|
+
challengeUrl,
|
|
2475
|
+
verifyUrl,
|
|
2476
|
+
post: (url, body) => postJson(url, body),
|
|
2477
|
+
isAbandoned: () => !live2(),
|
|
2478
|
+
logout: async () => {
|
|
2479
|
+
const out = this.#attr("logout-url");
|
|
2480
|
+
if (out) await fetch(out, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: "{}" }).catch(() => void 0);
|
|
2481
|
+
},
|
|
2482
|
+
onSignIn: () => {
|
|
2483
|
+
if (!live2() || this.#phase !== "signed-out" || this.#busy) return;
|
|
2484
|
+
this.#viaMetanet = true;
|
|
2485
|
+
this.#busy = true;
|
|
2486
|
+
this.#pendingLabel = PENDING_LABEL_OTHER;
|
|
2487
|
+
this.#set("pending");
|
|
2488
|
+
},
|
|
2489
|
+
onAttempt: (a) => this.#emit("bwalletx-attempt", a)
|
|
2490
|
+
});
|
|
2491
|
+
if (!live2()) return;
|
|
2492
|
+
this.#viaMetanet = false;
|
|
2493
|
+
this.#busy = false;
|
|
2494
|
+
if (!r) {
|
|
2495
|
+
if (this.#phase === "pending") this.#set("signed-out");
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2498
|
+
this.#signedType = r.walletType;
|
|
2499
|
+
this.#signedIn({ identityKey: r.identityKey, handle: r.handle, paymail: r.paymail });
|
|
2500
|
+
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 });
|
|
2501
|
+
}
|
|
2502
|
+
/** The wallet type the Switch note judges: the provider's, else what signed in here, else the remembered one. */
|
|
2503
|
+
#walletType() {
|
|
2504
|
+
if (this.#controlled) return this.#attr("wallet-type");
|
|
2505
|
+
return this.#signedType ?? (this.#remember ? rememberedWalletChoice()?.type ?? null : null);
|
|
2506
|
+
}
|
|
2507
|
+
#switchNoteShown(size) {
|
|
2508
|
+
const a = this.#attr("switch-note");
|
|
2509
|
+
if (a === "off" || !tapGoesElsewhere(this.#walletType())) return false;
|
|
2510
|
+
return a === "on" || size === "lg" || size === "md";
|
|
2511
|
+
}
|
|
2512
|
+
/**
|
|
2513
|
+
* Signed in: note the wallet type, and remember it for this origin when the user picked it (0.6.0), plus the
|
|
2514
|
+
* identity key as known here (0.7.0, public keys only: Metanet's silent attempt goes on only for these). Pass a key
|
|
2515
|
+
* only after the site's verify route accepted it.
|
|
2516
|
+
*/
|
|
2517
|
+
#picked(option, reason, identityKey) {
|
|
2518
|
+
this.#signedType = walletTypeOf(option);
|
|
2519
|
+
if (this.#remember && isExplicitPick(option, reason)) rememberWalletChoice(option);
|
|
2520
|
+
if (this.#remember) rememberKnownIdentityKey(identityKey);
|
|
2521
|
+
}
|
|
1991
2522
|
#attempt(option, outcome, started) {
|
|
1992
2523
|
this.#emit("bwalletx-attempt", attemptFor(option, outcome, started));
|
|
1993
2524
|
}
|
|
@@ -1996,16 +2527,24 @@ function createClass() {
|
|
|
1996
2527
|
* when another wallet is here or the user asked; otherwise 'not-found'. null: the chooser was closed.
|
|
1997
2528
|
*/
|
|
1998
2529
|
async #choose(mode, ms, pairing) {
|
|
2530
|
+
if (mode === "metanet") return { option: metanetOption(), reason: "requested" };
|
|
2531
|
+
const r = mode === "signin" && this.#remember ? rememberedWalletChoice() : null;
|
|
2532
|
+
if (r && r.type !== METANET_TYPE && !(r.type === BWALLETX_TYPE && r.substrate !== "pairing")) {
|
|
2533
|
+
const m = matchRemembered(await discoverWallets({ ms, pairing: () => pairing }), r);
|
|
2534
|
+
if (m) return { option: m, reason: "remembered" };
|
|
2535
|
+
}
|
|
1999
2536
|
if (mode !== "chooser") {
|
|
2000
2537
|
const f = await discoverBwalletX(ms);
|
|
2001
2538
|
if (f) {
|
|
2002
2539
|
const substrate = f.rdns === RDNS_EXTENSION ? "extension" : f.rdns === RDNS_MOBILE ? "in-app" : "xdm";
|
|
2003
|
-
|
|
2540
|
+
const option = brc100Option(f.wallet, { id: f.rdns ?? "bwalletx-xdm", rdns: f.rdns ?? void 0, name: "bWalletX", isBwalletX: true, substrate });
|
|
2541
|
+
return { option, reason: "bwalletx" };
|
|
2004
2542
|
}
|
|
2005
2543
|
}
|
|
2006
2544
|
const list = await discoverWallets({ ms: 50, pairing: () => pairing });
|
|
2007
2545
|
if (mode !== "chooser" && !hasBwalletX(list) && !otherDetected(list).length) return "not-found";
|
|
2008
|
-
|
|
2546
|
+
const picked = await chooseWallet(list, { getUrl: this.#attr("get-url") ?? void 0 });
|
|
2547
|
+
return picked ? { option: picked, reason: "chooser" } : null;
|
|
2009
2548
|
}
|
|
2010
2549
|
async #click(mode = "signin") {
|
|
2011
2550
|
if (this.hasAttribute("disabled") || this.hasAttribute("loading") || this.#busy || this.state === "pending") return;
|
|
@@ -2029,9 +2568,12 @@ function createClass() {
|
|
|
2029
2568
|
const started = Date.now();
|
|
2030
2569
|
const pairing = pairingModal();
|
|
2031
2570
|
let option = null;
|
|
2571
|
+
const run = ++this.#run;
|
|
2572
|
+
const live2 = () => run === this.#run;
|
|
2032
2573
|
this.#notFound = false;
|
|
2033
2574
|
this.#busy = true;
|
|
2034
|
-
this.#
|
|
2575
|
+
this.#viaMetanet = mode === "metanet";
|
|
2576
|
+
this.#pendingLabel = mode === "metanet" ? PENDING_LABEL_OTHER : PENDING_LABEL;
|
|
2035
2577
|
if (own) this.#set("pending");
|
|
2036
2578
|
else this.#render();
|
|
2037
2579
|
try {
|
|
@@ -2050,12 +2592,14 @@ function createClass() {
|
|
|
2050
2592
|
this.#attempt(null, "cancelled", started);
|
|
2051
2593
|
return;
|
|
2052
2594
|
}
|
|
2053
|
-
option = picked;
|
|
2595
|
+
option = picked.option;
|
|
2596
|
+
const reason = picked.reason;
|
|
2054
2597
|
if (!option.isBwalletX) {
|
|
2055
2598
|
this.#pendingLabel = PENDING_LABEL_OTHER;
|
|
2056
2599
|
if (own) this.#render();
|
|
2057
2600
|
}
|
|
2058
2601
|
const cw = await option.connect({ identity: false });
|
|
2602
|
+
if (!live2()) return;
|
|
2059
2603
|
const wallet = cw.wallet;
|
|
2060
2604
|
if (!wallet) throw Object.assign(new Error("This wallet cannot sign in here"), { code: "NOT_SUPPORTED" });
|
|
2061
2605
|
const sub = substrateOf(option);
|
|
@@ -2073,6 +2617,7 @@ function createClass() {
|
|
|
2073
2617
|
challenge: (identityKey) => postJson(challengeUrl, { identityKey, require: hint }),
|
|
2074
2618
|
verify: verifyUrl2 ? (response2) => postAccess(verifyUrl2, response2) : void 0
|
|
2075
2619
|
});
|
|
2620
|
+
this.#picked(option, reason, verifyUrl2 ? a.response.identityKey : null);
|
|
2076
2621
|
this.#attempt(option, "signed-in", started);
|
|
2077
2622
|
this.#emit("bwalletx-access-granted", { identityKey: a.response.identityKey, response: a.verified });
|
|
2078
2623
|
} catch (e) {
|
|
@@ -2085,18 +2630,29 @@ function createClass() {
|
|
|
2085
2630
|
const verifyUrl = this.#attr("verify-url");
|
|
2086
2631
|
const { result: r, verified } = await signInAndVerify({
|
|
2087
2632
|
wallet,
|
|
2633
|
+
// Metanet hands over its key on connect (one request at a time), so its sign-in is the signature alone.
|
|
2634
|
+
identityKey: cw.identityKey ?? void 0,
|
|
2635
|
+
skipWaitForAuthentication: option.id === METANET_OPTION_ID,
|
|
2636
|
+
// its connect() just checked the lock
|
|
2088
2637
|
identityKeyHint: verifyUrl ? rememberedIdentity() : void 0,
|
|
2089
2638
|
challenge: (identityKey) => postJson(challengeUrl, identityKey ? { identityKey } : {}),
|
|
2090
2639
|
verify: async (res) => verifyUrl ? await postJson(verifyUrl, { identityKey: res.identityKey, nonce: res.nonce, signature: res.signature }) : {}
|
|
2091
2640
|
});
|
|
2641
|
+
if (!live2()) {
|
|
2642
|
+
const out = this.#attr("logout-url");
|
|
2643
|
+
if (verifyUrl && out) await fetch(out, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: "{}" }).catch(() => void 0);
|
|
2644
|
+
return;
|
|
2645
|
+
}
|
|
2092
2646
|
if (verifyUrl) rememberIdentity(r.identityKey);
|
|
2093
2647
|
const response = verifyUrl ? verified : null;
|
|
2648
|
+
this.#picked(option, reason, verifyUrl ? r.identityKey : null);
|
|
2094
2649
|
if (own) this.#signedIn({ identityKey: r.identityKey, handle: response?.handle ?? null, paymail: response?.paymail ?? null });
|
|
2095
2650
|
this.#attempt(option, "signed-in", started);
|
|
2096
2651
|
this.#emit("bwalletx-signed-in", { identityKey: r.identityKey, method, nonce: r.nonce, signature: r.signature, response });
|
|
2097
2652
|
} catch (error) {
|
|
2098
2653
|
const outcome = outcomeOf(error);
|
|
2099
2654
|
this.#attempt(option, outcome, started);
|
|
2655
|
+
if (!live2()) return;
|
|
2100
2656
|
const closedQr = outcome === "cancelled" && option?.kind === "pairing";
|
|
2101
2657
|
if (own) {
|
|
2102
2658
|
if (closedQr) this.#phase = prev;
|
|
@@ -2107,12 +2663,15 @@ function createClass() {
|
|
|
2107
2663
|
}
|
|
2108
2664
|
if (!closedQr) this.#emit("bwalletx-error", { error });
|
|
2109
2665
|
} finally {
|
|
2110
|
-
this.#busy = false;
|
|
2111
2666
|
try {
|
|
2112
2667
|
pairing.close();
|
|
2113
2668
|
} catch {
|
|
2114
2669
|
}
|
|
2115
|
-
|
|
2670
|
+
if (live2()) {
|
|
2671
|
+
this.#busy = false;
|
|
2672
|
+
this.#viaMetanet = false;
|
|
2673
|
+
this.#render();
|
|
2674
|
+
}
|
|
2116
2675
|
}
|
|
2117
2676
|
}
|
|
2118
2677
|
};
|
|
@@ -2129,6 +2688,16 @@ function defineBwalletxSignin(tag = SIGNIN_TAG) {
|
|
|
2129
2688
|
// src/wallet-button.ts
|
|
2130
2689
|
var BUTTON_TAG = "bwalletx-button";
|
|
2131
2690
|
var CONNECTED_KEY = "bwx:connected";
|
|
2691
|
+
function migrateConnectedKey() {
|
|
2692
|
+
const had = dropLegacyConnectedKey();
|
|
2693
|
+
if (had && !rememberedWalletChoice()) rememberBwalletX("extension");
|
|
2694
|
+
return had;
|
|
2695
|
+
}
|
|
2696
|
+
function rememberBwalletX(substrate) {
|
|
2697
|
+
rememberWalletChoice({ id: "bwalletx", name: "bWalletX", kind: substrate === "pairing" ? "pairing" : "brc100", isBwalletX: true, substrate, connect: async () => {
|
|
2698
|
+
throw new Error("unused");
|
|
2699
|
+
} });
|
|
2700
|
+
}
|
|
2132
2701
|
function dropLegacyConnectedKey() {
|
|
2133
2702
|
try {
|
|
2134
2703
|
const had = globalThis.localStorage?.getItem(CONNECTED_KEY) != null;
|
|
@@ -2198,7 +2767,7 @@ function createClass2() {
|
|
|
2198
2767
|
}
|
|
2199
2768
|
live.add(this);
|
|
2200
2769
|
this.#hideLegacy();
|
|
2201
|
-
|
|
2770
|
+
migrateConnectedKey();
|
|
2202
2771
|
const phone = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
|
2203
2772
|
this.#set(stateFor({ connected: false, found: null, phone }));
|
|
2204
2773
|
const ms = Number(this.getAttribute("discover-ms")) || 800;
|
|
@@ -2265,8 +2834,9 @@ function createClass2() {
|
|
|
2265
2834
|
const modal = pairingModal({ title: "Connect bWalletX" });
|
|
2266
2835
|
this.#set("connecting");
|
|
2267
2836
|
try {
|
|
2268
|
-
const { wallet } = await connectBwalletX({ pairing: modal });
|
|
2837
|
+
const { wallet, method } = await connectBwalletX({ pairing: modal });
|
|
2269
2838
|
const { publicKey } = await wallet.getPublicKey({ identityKey: true });
|
|
2839
|
+
rememberBwalletX(method === "pairing" ? "pairing" : method === "in-app" ? "in-app" : "extension");
|
|
2270
2840
|
this.#connected(publicKey);
|
|
2271
2841
|
} catch (e) {
|
|
2272
2842
|
if (e?.code === "NOT_FOUND") this.#set("none");
|
|
@@ -2293,6 +2863,7 @@ function createClass2() {
|
|
|
2293
2863
|
this.#key = null;
|
|
2294
2864
|
this.#paymail = null;
|
|
2295
2865
|
dropLegacyConnectedKey();
|
|
2866
|
+
if (rememberedWalletChoice()?.type === BWALLETX_TYPE) forgetWalletChoice();
|
|
2296
2867
|
this.#set("available");
|
|
2297
2868
|
this.#emit("bwalletx-disconnected");
|
|
2298
2869
|
}
|
|
@@ -2326,8 +2897,17 @@ function defineBwalletxButton(tag = BUTTON_TAG) {
|
|
|
2326
2897
|
}
|
|
2327
2898
|
|
|
2328
2899
|
// src/pay.ts
|
|
2329
|
-
var
|
|
2900
|
+
var import_sdk7 = require("@bsv/sdk");
|
|
2330
2901
|
var BRC29_PROTOCOL = [2, "3241645161d8"];
|
|
2902
|
+
var NO_COINS_TEXT = "Your wallet doesn't have enough to cover this payment and its small network fee. Add a little and try again.";
|
|
2903
|
+
function isInsufficientFunds(e) {
|
|
2904
|
+
const err = e;
|
|
2905
|
+
if (!err) return false;
|
|
2906
|
+
if (err.name === "WERR_INSUFFICIENT_FUNDS" || err.code === "WERR_INSUFFICIENT_FUNDS" || err.code === "ERR_INSUFFICIENT_FUNDS") return true;
|
|
2907
|
+
if (err.isError === true && err.code === 7) return true;
|
|
2908
|
+
const msg = typeof err.message === "string" ? err.message : String(e);
|
|
2909
|
+
return /insufficient (funds|balance)|not enough (funds|coins|satoshis|sats)|more satoshis are needed/i.test(msg);
|
|
2910
|
+
}
|
|
2331
2911
|
var PaymentError = class extends Error {
|
|
2332
2912
|
constructor(code, message, maybeSent = false, cause) {
|
|
2333
2913
|
super(message);
|
|
@@ -2346,13 +2926,13 @@ var isPaymail = (s) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s);
|
|
|
2346
2926
|
function randomB64(n) {
|
|
2347
2927
|
const b = new Uint8Array(n);
|
|
2348
2928
|
globalThis.crypto.getRandomValues(b);
|
|
2349
|
-
return
|
|
2929
|
+
return import_sdk7.Utils.toBase64(Array.from(b));
|
|
2350
2930
|
}
|
|
2351
2931
|
function opReturn(text) {
|
|
2352
|
-
const d =
|
|
2932
|
+
const d = import_sdk7.Utils.toArray(text, "utf8");
|
|
2353
2933
|
const len = d.length;
|
|
2354
2934
|
const push = len < 76 ? [len] : len <= 255 ? [76, len] : [77, len & 255, len >> 8];
|
|
2355
|
-
return
|
|
2935
|
+
return import_sdk7.Utils.toHex([0, 106, ...push, ...d]);
|
|
2356
2936
|
}
|
|
2357
2937
|
var clip = (s, n) => s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
|
|
2358
2938
|
var describe = (s) => clip(s.length < 5 ? `${s} payment`.trim() : s, 50);
|
|
@@ -2423,6 +3003,7 @@ async function runAction(wallet, description, outputs, labels) {
|
|
|
2423
3003
|
options: { randomizeOutputs: false }
|
|
2424
3004
|
});
|
|
2425
3005
|
} catch (e) {
|
|
3006
|
+
if (isInsufficientFunds(e)) throw new PaymentError("INSUFFICIENT_FUNDS", NO_COINS_TEXT, false, e);
|
|
2426
3007
|
const msg = e?.message ?? String(e);
|
|
2427
3008
|
const rejected = /denied|reject|cancel|abort|user/i.test(msg) || e?.code === 4001;
|
|
2428
3009
|
throw new PaymentError(rejected ? "WALLET_REJECTED" : "WALLET_ERROR", rejected ? "The payment was declined in the wallet." : `The wallet could not make the payment: ${msg}`, !rejected, e);
|
|
@@ -2448,7 +3029,7 @@ async function pay(source, req, o = {}) {
|
|
|
2448
3029
|
} catch (e) {
|
|
2449
3030
|
throw new PaymentError("WALLET_ERROR", `The wallet could not derive the payment key: ${e.message}`, false, e);
|
|
2450
3031
|
}
|
|
2451
|
-
outputs = [{ script: new
|
|
3032
|
+
outputs = [{ script: new import_sdk7.P2PKH().lock(import_sdk7.PublicKey.fromString(pk).toHash()).toHex(), satoshis: sats, description: "payment" }];
|
|
2452
3033
|
remittance = { senderIdentityKey: me, derivationPrefix, derivationSuffix, outputIndex: 0 };
|
|
2453
3034
|
} else if (isPaymail(to)) {
|
|
2454
3035
|
const r2 = await (o.resolvePaymail ?? ((p, s) => resolvePaymailP2P(p, s, o.fetch ?? fetch)))(to, sats);
|
|
@@ -2456,7 +3037,7 @@ async function pay(source, req, o = {}) {
|
|
|
2456
3037
|
submit = r2.submit;
|
|
2457
3038
|
} else {
|
|
2458
3039
|
try {
|
|
2459
|
-
outputs = [{ script: new
|
|
3040
|
+
outputs = [{ script: new import_sdk7.P2PKH().lock(to).toHex(), satoshis: sats, description: "payment" }];
|
|
2460
3041
|
} catch {
|
|
2461
3042
|
throw new PaymentError("BAD_REQUEST", `Not a paymail, address or identity key: ${to}`);
|
|
2462
3043
|
}
|
|
@@ -2466,7 +3047,7 @@ async function pay(source, req, o = {}) {
|
|
|
2466
3047
|
if (!r.txid) throw new PaymentError("WALLET_ERROR", "The wallet did not return a txid", true);
|
|
2467
3048
|
const tx = r.tx ? Array.from(r.tx) : void 0;
|
|
2468
3049
|
if (submit && tx) {
|
|
2469
|
-
await submit(
|
|
3050
|
+
await submit(import_sdk7.Transaction.fromAtomicBEEF(tx).toHex()).catch(() => {
|
|
2470
3051
|
});
|
|
2471
3052
|
}
|
|
2472
3053
|
return { txid: r.txid, tx, sats, outputs, remittance };
|
|
@@ -2504,54 +3085,6 @@ function declareWalletTheme(theme) {
|
|
|
2504
3085
|
}
|
|
2505
3086
|
if (meta.content !== id) meta.content = id;
|
|
2506
3087
|
}
|
|
2507
|
-
|
|
2508
|
-
// src/connect-flow.ts
|
|
2509
|
-
async function runConnect(o) {
|
|
2510
|
-
const started = Date.now();
|
|
2511
|
-
const { list } = o;
|
|
2512
|
-
let choice = null;
|
|
2513
|
-
let reason = "none";
|
|
2514
|
-
try {
|
|
2515
|
-
const d = decideWallet(list, { walletId: o.walletId, chooser: o.chooser, picker: o.picker, fastPath: o.fastPath });
|
|
2516
|
-
choice = d.option;
|
|
2517
|
-
reason = d.reason;
|
|
2518
|
-
if (!choice && d.reason === "chooser") choice = await o.pick(list);
|
|
2519
|
-
if (!choice) {
|
|
2520
|
-
if (d.reason === "none" || !list.length) throw Object.assign(new Error("No wallet found. Get bWalletX at bwalletx.com."), { code: "NOT_FOUND" });
|
|
2521
|
-
reportAttempt(o.onAttempt, attemptFor(null, "cancelled", started));
|
|
2522
|
-
return null;
|
|
2523
|
-
}
|
|
2524
|
-
const signIn = !!(o.challengeUrl && o.verifyUrl) && choice.kind !== "handcash";
|
|
2525
|
-
const cw = await choice.connect(signIn ? { identity: false } : void 0);
|
|
2526
|
-
let handle = cw.handle ?? null;
|
|
2527
|
-
let paymail = cw.paymail ?? null;
|
|
2528
|
-
let identityKey = cw.identityKey;
|
|
2529
|
-
let signedIn = false;
|
|
2530
|
-
if (cw.wallet && signIn) {
|
|
2531
|
-
const { result, verified } = await signInAndVerify({
|
|
2532
|
-
wallet: cw.wallet,
|
|
2533
|
-
identityKey: cw.identityKey ?? void 0,
|
|
2534
|
-
// The key that signed in here last time: a repeat sign-in is then the signature alone.
|
|
2535
|
-
identityKeyHint: rememberedIdentity(),
|
|
2536
|
-
challengeNeedsIdentityKey: o.challengeNeedsIdentityKey,
|
|
2537
|
-
challenge: (key) => o.post(o.challengeUrl, key ? { identityKey: key } : {}),
|
|
2538
|
-
verify: (r) => o.post(o.verifyUrl, { identityKey: r.identityKey, nonce: r.nonce, signature: r.signature })
|
|
2539
|
-
});
|
|
2540
|
-
rememberIdentity(result.identityKey);
|
|
2541
|
-
identityKey = result.identityKey;
|
|
2542
|
-
handle = verified.handle ?? handle;
|
|
2543
|
-
paymail = verified.paymail ?? paymail;
|
|
2544
|
-
signedIn = true;
|
|
2545
|
-
} else if (cw.wallet && !identityKey) {
|
|
2546
|
-
identityKey = (await cw.wallet.getPublicKey({ identityKey: true })).publicKey;
|
|
2547
|
-
}
|
|
2548
|
-
reportAttempt(o.onAttempt, attemptFor(choice, signedIn ? "signed-in" : "connected", started));
|
|
2549
|
-
return { connected: cw, identityKey, handle, paymail, signedIn, walletType: walletTypeOf(choice), substrate: substrateOf(choice), reason };
|
|
2550
|
-
} catch (e) {
|
|
2551
|
-
reportAttempt(o.onAttempt, attemptFor(choice, outcomeOf(e), started));
|
|
2552
|
-
throw e;
|
|
2553
|
-
}
|
|
2554
|
-
}
|
|
2555
3088
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2556
3089
|
0 && (module.exports = {
|
|
2557
3090
|
ACCESS_ERROR_STATUS,
|
|
@@ -2564,13 +3097,27 @@ async function runConnect(o) {
|
|
|
2564
3097
|
BWALLETX_RDNS,
|
|
2565
3098
|
BWALLETX_TYPE,
|
|
2566
3099
|
CONNECTED_KEY,
|
|
3100
|
+
CONTINUE_OTHER_LABEL,
|
|
2567
3101
|
DEFAULT_DISCOVER_MS,
|
|
2568
3102
|
DESKTOP_OPTION_ID,
|
|
2569
3103
|
GET_URL,
|
|
2570
3104
|
IDENTITY_HINT_KEY,
|
|
3105
|
+
KNOWN_KEYS_KEY,
|
|
2571
3106
|
LOGIN_PROTOCOL,
|
|
2572
3107
|
LOGO_URL,
|
|
2573
3108
|
MENU_CSS,
|
|
3109
|
+
METANET_ANSWER_MS,
|
|
3110
|
+
METANET_JSON_API,
|
|
3111
|
+
METANET_LABEL,
|
|
3112
|
+
METANET_NOTE,
|
|
3113
|
+
METANET_OPTION_ID,
|
|
3114
|
+
METANET_REMEMBERED_NOTE,
|
|
3115
|
+
METANET_SILENT_MS,
|
|
3116
|
+
METANET_TYPE,
|
|
3117
|
+
METANET_UNREACHABLE,
|
|
3118
|
+
MetanetUnreachableError,
|
|
3119
|
+
NOT_YOU_LABEL,
|
|
3120
|
+
NO_COINS_TEXT,
|
|
2574
3121
|
PAYMAIL_LOOKUP_URL,
|
|
2575
3122
|
PENDING_LABEL,
|
|
2576
3123
|
PENDING_LABEL_OTHER,
|
|
@@ -2581,11 +3128,14 @@ async function runConnect(o) {
|
|
|
2581
3128
|
RDNS_MOBILE,
|
|
2582
3129
|
SIGNIN_CSS,
|
|
2583
3130
|
SIGNIN_TAG,
|
|
3131
|
+
SWITCH_NOTE,
|
|
3132
|
+
WALLET_CHOICE_KEY,
|
|
2584
3133
|
WALLET_THEME_META,
|
|
2585
3134
|
WEB_WALLET_URL,
|
|
2586
3135
|
accessErrorMessage,
|
|
2587
3136
|
accountLabel,
|
|
2588
3137
|
amountInSats,
|
|
3138
|
+
attachRemembered,
|
|
2589
3139
|
attemptFor,
|
|
2590
3140
|
autoChoice,
|
|
2591
3141
|
avatarUrlFor,
|
|
@@ -2597,8 +3147,10 @@ async function runConnect(o) {
|
|
|
2597
3147
|
challengeWantsIdentityKey,
|
|
2598
3148
|
chooseWallet,
|
|
2599
3149
|
chooserOrder,
|
|
3150
|
+
claimSilentAttempt,
|
|
2600
3151
|
clearPairing,
|
|
2601
3152
|
connectBwalletX,
|
|
3153
|
+
connectMetanetDesktop,
|
|
2602
3154
|
cwiIsAnnounced,
|
|
2603
3155
|
decideWallet,
|
|
2604
3156
|
declareWalletTheme,
|
|
@@ -2609,19 +3161,33 @@ async function runConnect(o) {
|
|
|
2609
3161
|
discoverWallets,
|
|
2610
3162
|
dropLegacyConnectedKey,
|
|
2611
3163
|
findBwalletX,
|
|
3164
|
+
forgetWalletChoice,
|
|
2612
3165
|
getUrlFor,
|
|
2613
3166
|
handcashAdapter,
|
|
2614
3167
|
handleLabel,
|
|
2615
3168
|
hasBwalletX,
|
|
3169
|
+
inAnyFrame,
|
|
2616
3170
|
inOrder,
|
|
3171
|
+
isExplicitPick,
|
|
2617
3172
|
isFramed,
|
|
2618
3173
|
isIdentityKeyHex,
|
|
3174
|
+
isInsufficientFunds,
|
|
3175
|
+
isKnownIdentityKey,
|
|
3176
|
+
isLocalDesktop,
|
|
3177
|
+
isLocalDesktopType,
|
|
2619
3178
|
isPaymail,
|
|
2620
3179
|
isPresentBwalletX,
|
|
3180
|
+
isRememberable,
|
|
2621
3181
|
isTouchDevice,
|
|
2622
3182
|
isUserRejection,
|
|
3183
|
+
knownIdentityKeys,
|
|
2623
3184
|
lookupPaymail,
|
|
2624
3185
|
markSvg,
|
|
3186
|
+
matchRemembered,
|
|
3187
|
+
metanetOffered,
|
|
3188
|
+
metanetOption,
|
|
3189
|
+
migrateConnectedKey,
|
|
3190
|
+
needsBwalletX,
|
|
2625
3191
|
otherDetected,
|
|
2626
3192
|
outcomeOf,
|
|
2627
3193
|
pairBwalletX,
|
|
@@ -2629,29 +3195,39 @@ async function runConnect(o) {
|
|
|
2629
3195
|
pairingOption,
|
|
2630
3196
|
pay,
|
|
2631
3197
|
pickWallet,
|
|
3198
|
+
planAutoConnect,
|
|
2632
3199
|
prewarmBwalletX,
|
|
2633
3200
|
protocolMediaUrl,
|
|
2634
3201
|
proveHoldings,
|
|
2635
3202
|
qrSvg,
|
|
2636
3203
|
rememberIdentity,
|
|
3204
|
+
rememberKnownIdentityKey,
|
|
3205
|
+
rememberWalletChoice,
|
|
2637
3206
|
rememberedIdentity,
|
|
3207
|
+
rememberedWalletChoice,
|
|
2638
3208
|
renderButton,
|
|
2639
3209
|
reportAttempt,
|
|
2640
3210
|
requestAccess,
|
|
3211
|
+
resetSilentAttempt,
|
|
2641
3212
|
resolvePaymailP2P,
|
|
2642
3213
|
restorePairing,
|
|
2643
3214
|
runConnect,
|
|
3215
|
+
runSilentMetanet,
|
|
2644
3216
|
shortKey,
|
|
2645
3217
|
signInAndVerify,
|
|
2646
3218
|
signInErrorText,
|
|
2647
3219
|
signInWithBwalletX,
|
|
2648
3220
|
signinText,
|
|
3221
|
+
silentAttemptUsed,
|
|
2649
3222
|
stateFor,
|
|
2650
3223
|
substrateOf,
|
|
3224
|
+
tapGoesElsewhere,
|
|
2651
3225
|
throwIfAccessError,
|
|
2652
3226
|
tip,
|
|
2653
3227
|
toAccessError,
|
|
3228
|
+
trySilentMetanet,
|
|
2654
3229
|
waitIfLocked,
|
|
2655
|
-
walletTypeOf
|
|
3230
|
+
walletTypeOf,
|
|
3231
|
+
warnIfNoLogout
|
|
2656
3232
|
});
|
|
2657
3233
|
//# sourceMappingURL=index.cjs.map
|