@decentnetwork/beagle-ui 0.2.32 → 0.2.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/beagle-ui.js CHANGED
@@ -1,4 +1,4 @@
1
- export const BEAGLE_UI_VERSION = "0.2.32";
1
+ export const BEAGLE_UI_VERSION = "0.2.33";
2
2
  // src/icons.jsx
3
3
  var ICON_PATHS = {
4
4
  // ---- tab bar (the four must feel like one set) ----
@@ -125,6 +125,8 @@ var hostNeedsWelcome = false;
125
125
  var hostPrefersRtcFile = false;
126
126
  var hostHasKeyBackup = false;
127
127
  var hostEnsWrite = true;
128
+ var hostWalletBind = true;
129
+ var hostAssistant = true;
128
130
  var hostBrandHref = "";
129
131
  var hostTabs = null;
130
132
  var hostHideApps = null;
@@ -144,6 +146,8 @@ function setUiHost(host) {
144
146
  hostPrefersRtcFile = !!H.hostPrefersRtcFile;
145
147
  hostHasKeyBackup = !!H.hostHasKeyBackup;
146
148
  hostEnsWrite = H.hostEnsWrite !== void 0 ? !!H.hostEnsWrite : !H.hostNeedsWelcome;
149
+ hostWalletBind = H.hostWalletBind !== void 0 ? !!H.hostWalletBind : hostEnsWrite;
150
+ hostAssistant = H.hostAssistant !== void 0 ? !!H.hostAssistant : true;
147
151
  hostBrandHref = typeof H.hostBrandHref === "string" ? H.hostBrandHref : "";
148
152
  }
149
153
  function fileUrl(name) {
@@ -3501,6 +3505,67 @@ function ChatTab({ T, lang, peers, requests, activeId, thread, isProtocol, proto
3501
3505
  }
3502
3506
  Object.assign(window, { ChatTab });
3503
3507
 
3508
+ // src/wallet-proof.js
3509
+ var PROOF_KEY = { eth: "proof.eth", sol: "proof.sol" };
3510
+ var HEAD = "Beagle wallet binding";
3511
+ var LINE = "I bind this wallet to my Beagle identity.";
3512
+ var ADDRESS_RE = { eth: /^0x[0-9a-fA-F]{40}$/, sol: /^[1-9A-HJ-NP-Za-km-z]{32,44}$/ };
3513
+ var USERID_RE = /^[1-9A-HJ-NP-Za-km-z]{32,48}$/;
3514
+ var ISSUED_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
3515
+ function isWalletAddress(chain, address) {
3516
+ const re = ADDRESS_RE[chain];
3517
+ return !!re && typeof address === "string" && re.test(address);
3518
+ }
3519
+ function proofTime(date = /* @__PURE__ */ new Date()) {
3520
+ return new Date(Math.floor(date.getTime() / 1e3) * 1e3).toISOString().replace(".000Z", "Z");
3521
+ }
3522
+ function walletProofMessage({ userid, chain, address, issued }) {
3523
+ if (!USERID_RE.test(String(userid || "")))
3524
+ throw new Error("wallet proof: bad userid");
3525
+ if (!isWalletAddress(chain, address))
3526
+ throw new Error("wallet proof: bad chain or address");
3527
+ if (!ISSUED_RE.test(String(issued || "")))
3528
+ throw new Error("wallet proof: bad issued time");
3529
+ return [HEAD, "", LINE, "", `userid: ${userid}`, `chain: ${chain}`, `address: ${address}`, `issued: ${issued}`].join("\n");
3530
+ }
3531
+ var B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
3532
+ function base58Encode(bytes) {
3533
+ const digits = [];
3534
+ for (const b of bytes) {
3535
+ let carry = b;
3536
+ for (let j = 0; j < digits.length; j++) {
3537
+ carry += digits[j] << 8;
3538
+ digits[j] = carry % 58;
3539
+ carry = carry / 58 | 0;
3540
+ }
3541
+ while (carry > 0) {
3542
+ digits.push(carry % 58);
3543
+ carry = carry / 58 | 0;
3544
+ }
3545
+ }
3546
+ let out = "";
3547
+ for (const b of bytes) {
3548
+ if (b !== 0)
3549
+ break;
3550
+ out += "1";
3551
+ }
3552
+ for (let i = digits.length - 1; i >= 0; i--)
3553
+ out += B58[digits[i]];
3554
+ return out;
3555
+ }
3556
+ var utf8Hex = (s) => "0x" + Array.from(new TextEncoder().encode(s), (b) => b.toString(16).padStart(2, "0")).join("");
3557
+ async function signEthProof(provider, { userid, address, issued = proofTime() }) {
3558
+ const message = walletProofMessage({ userid, chain: "eth", address, issued });
3559
+ const signature = await provider.request({ method: "personal_sign", params: [utf8Hex(message), address] });
3560
+ return { message, signature: String(signature) };
3561
+ }
3562
+ async function signSolProof(wallet, { userid, address, issued = proofTime() }) {
3563
+ const message = walletProofMessage({ userid, chain: "sol", address, issued });
3564
+ const r = await wallet.signMessage(new TextEncoder().encode(message), "utf8");
3565
+ const sig = r && r.signature ? r.signature : r;
3566
+ return { message, signature: base58Encode(Uint8Array.from(sig)) };
3567
+ }
3568
+
3504
3569
  // src/profile.jsx
3505
3570
  function CopyBtn({ value, copiedText = "Copied", copyFailedText = "Copy failed", copyTitle = "Copy" }) {
3506
3571
  const [status, setStatus] = React.useState(null);
@@ -3915,7 +3980,7 @@ function DkEnsCard({ T, me, onRecord }) {
3915
3980
  const [msg, setMsg] = React.useState(null);
3916
3981
  const reload = () => {
3917
3982
  fetch("/api/ens-name").then((r) => r.json()).then((d) => {
3918
- setSt({ loading: false, registered: !!d.registered, record: d.record || null, mineOwned: !!d.mineOwned });
3983
+ setSt({ loading: false, registered: !!d.registered, record: d.record || null, mineOwned: !!d.mineOwned, writeBlocked: d.writeBlocked || "" });
3919
3984
  if (d.registered && d.record && d.record.name)
3920
3985
  setLabel(d.record.name.replace(/\.beagles\.eth$/i, ""));
3921
3986
  if (onRecord)
@@ -3942,7 +4007,15 @@ function DkEnsCard({ T, me, onRecord }) {
3942
4007
  if (l)
3943
4008
  post("/api/ens-register", { name: l, displayName: me.name, description: me.description || "" });
3944
4009
  };
3945
- const bind = (chain, address) => post("/api/ens-bind-wallet", { chain, address });
4010
+ const bind = (chain, address, proof) => post("/api/ens-bind-wallet", proof ? { chain, address, proof } : { chain, address });
4011
+ const signing = () => {
4012
+ setBusy(true);
4013
+ setMsg({ tone: "ok", text: T.ensSigning || "approve the signature in your wallet\u2026" });
4014
+ };
4015
+ const walletErr = (e) => {
4016
+ setBusy(false);
4017
+ setMsg({ tone: "err", text: String(e && e.message || e) });
4018
+ };
3946
4019
  const bindEth = async () => {
3947
4020
  const eth = window.ethereum;
3948
4021
  if (!eth) {
@@ -3951,10 +4024,13 @@ function DkEnsCard({ T, me, onRecord }) {
3951
4024
  }
3952
4025
  try {
3953
4026
  const a = await eth.request({ method: "eth_requestAccounts" });
3954
- if (a && a[0])
3955
- bind("eth", a[0]);
4027
+ if (!a || !a[0])
4028
+ return;
4029
+ signing();
4030
+ const proof = await signEthProof(eth, { userid: me.userId, address: a[0] });
4031
+ bind("eth", a[0], proof);
3956
4032
  } catch (e) {
3957
- setMsg({ tone: "err", text: String(e && e.message || e) });
4033
+ walletErr(e);
3958
4034
  }
3959
4035
  };
3960
4036
  const bindSol = async () => {
@@ -3966,10 +4042,13 @@ function DkEnsCard({ T, me, onRecord }) {
3966
4042
  try {
3967
4043
  const r = await sol.connect();
3968
4044
  const a = r && r.publicKey && r.publicKey.toString();
3969
- if (a)
3970
- bind("sol", a);
4045
+ if (!a)
4046
+ return;
4047
+ signing();
4048
+ const proof = await signSolProof(sol, { userid: me.userId, address: a });
4049
+ bind("sol", a, proof);
3971
4050
  } catch (e) {
3972
- setMsg({ tone: "err", text: String(e && e.message || e) });
4051
+ walletErr(e);
3973
4052
  }
3974
4053
  };
3975
4054
  const field = { flex: 1, minWidth: 0, height: 34, borderRadius: 8, border: "1px solid var(--line)", background: "var(--panel-2)", color: "var(--text)", fontFamily: "var(--mono)", fontSize: 13, padding: "0 10px", outline: "none" };
@@ -4018,6 +4097,7 @@ function DkEnsCard({ T, me, onRecord }) {
4018
4097
  const [picking, setPicking] = React.useState(false);
4019
4098
  const fileRef = React.useRef(null);
4020
4099
  const canWrite = hostEnsWrite && !!st.mineOwned;
4100
+ const canBind = hostWalletBind && !!st.mineOwned && !st.writeBlocked;
4021
4101
  const rec = st.record;
4022
4102
  const boundEth = rec && rec.addresses && rec.addresses["60"];
4023
4103
  const boundSol = rec && rec.addresses && rec.addresses["501"];
@@ -4040,7 +4120,16 @@ function DkEnsCard({ T, me, onRecord }) {
4040
4120
  setMsg({ tone: "err", text: m === "too-big" ? T.ensTooBig || "image too large even after scaling" : m === "bad-image" ? T.ensBadImage || "cannot read that image" : String(m || err) });
4041
4121
  });
4042
4122
  };
4043
- const walletRow = (lbl, chain, bound, onBind, last) => /* @__PURE__ */ React.createElement("div", { className: "dk-row", style: { ...row, borderBottom: last ? "none" : row.borderBottom } }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, lbl), bound ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, bound), /* @__PURE__ */ React.createElement(CopyBtn, { value: bound, copiedText: T.copied, copyFailedText: T.copyFailed, copyTitle: T.copy }), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !canWrite, onClick: () => bind(chain, "") }, T.ensUnbind || "unbind")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)" } }, T.ensNotBound || "not bound"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !canWrite, onClick: onBind }, T.ensBind || "bind")));
4123
+ const proven = (chain, bound) => {
4124
+ if (rec && rec.owner && String(rec.owner).toLowerCase() === String(bound).toLowerCase())
4125
+ return true;
4126
+ try {
4127
+ return JSON.parse((rec && rec.texts || {})[PROOF_KEY[chain]]).msg.indexOf("address: " + bound + "\n") >= 0;
4128
+ } catch {
4129
+ return false;
4130
+ }
4131
+ };
4132
+ const walletRow = (lbl, chain, bound, onBind, last) => /* @__PURE__ */ React.createElement("div", { className: "dk-row", style: { ...row, borderBottom: last ? "none" : row.borderBottom } }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, lbl), bound ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, bound), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--ui)", fontSize: 11, color: proven(chain, bound) ? "var(--online)" : "var(--faint)" } }, proven(chain, bound) ? T.ensProven || "signed by this wallet" : T.ensUnproven || "no wallet signature yet")), /* @__PURE__ */ React.createElement(CopyBtn, { value: bound, copiedText: T.copied, copyFailedText: T.copyFailed, copyTitle: T.copy }), !proven(chain, bound) && /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !canBind, onClick: onBind }, T.ensProve || "sign"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !canBind, onClick: () => bind(chain, "") }, T.ensUnbind || "unbind")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)" } }, T.ensNotBound || "not bound"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !canBind, onClick: onBind }, T.ensBind || "bind")));
4044
4133
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Card, { label: T.ensCard || "Name \xB7 beagles.eth" }, st.loading ? /* @__PURE__ */ React.createElement("div", { style: { padding: "14px 16px", fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)" } }, T.dirLoading || "loading\u2026") : /* @__PURE__ */ React.createElement(React.Fragment, null, !hostEnsWrite ? /* @__PURE__ */ React.createElement("div", { className: "dk-row dk-wrap", style: row }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.ensName || "name"), st.registered && rec && rec.name ? /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 13, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, rec.name) : /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--ui)", fontSize: 12, lineHeight: 1.5, color: "var(--faint)" } }, T.ensWebOnly || "Registering a beagles.eth name needs the Beagle phone or desktop app. Your avatar can be set above without one.")) : /* @__PURE__ */ React.createElement("div", { className: "dk-row dk-wrap", style: row }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.ensName || "name"), /* @__PURE__ */ React.createElement(
4045
4134
  "input",
4046
4135
  {
@@ -4054,7 +4143,7 @@ function DkEnsCard({ T, me, onRecord }) {
4054
4143
  },
4055
4144
  style: { ...field, minWidth: 110 }
4056
4145
  }
4057
- ), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--dim)", flexShrink: 0 } }, ".beagles.eth"), /* @__PURE__ */ React.createElement(Btn, { tone: "accent", size: "sm", disabled: busy || !label.trim(), onClick: register }, busy ? "\u2026" : st.registered && st.mineOwned ? T.ensUpdate || "update" : T.ensRegister || "register")), st.registered && !st.mineOwned && /* @__PURE__ */ React.createElement("div", { style: { padding: "10px 16px", borderBottom: "1px solid var(--line)", fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--faint)" } }, (T.ensWalletOwned || "registered via your mobile wallet as") + " ", /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)" } }, rec && rec.name)), st.registered && /* @__PURE__ */ React.createElement("div", { className: "dk-row dk-wrap", style: row }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.ensAvatar || "avatar"), upAvatar ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("img", { src: upAvatar, alt: "", width: 34, height: 34, style: { width: 34, height: 34, borderRadius: 9, objectFit: "cover", flexShrink: 0, background: "var(--panel-2)" } }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--text)" } }, T.ensCustom || "custom image"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !canWrite, onClick: () => post("/api/ens-avatar", { nftid: null }) }, T.ensClear || "clear")) : punkId != null ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DkPunkAvatar, { id: punkId, size: 34, radius: 9, fallbackSeed: me.userId }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)" } }, `CryptoPunk #${punkId}`), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !canWrite, onClick: () => post("/api/ens-avatar", { nftid: null }) }, T.ensClear || "clear")) : /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)" } }, T.ensNotSet || "not set"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !canWrite, onClick: () => fileRef.current && fileRef.current.click() }, T.ensUpload || "upload"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !canWrite, onClick: () => setPicking(true) }, T.ensChoose || "choose"), /* @__PURE__ */ React.createElement("input", { ref: fileRef, type: "file", accept: "image/*", onChange: onUploadFile, style: { display: "none" } })), walletRow(T.ensEth || "ethereum", "eth", boundEth, bindEth, false), walletRow(T.ensSol || "solana", "sol", boundSol, bindSol, !msg), msg && /* @__PURE__ */ React.createElement("div", { style: { padding: "10px 16px", fontFamily: "var(--ui)", fontSize: 12, color: msg.tone === "ok" ? "var(--online)" : "var(--danger)" } }, msg.text)), picking && /* @__PURE__ */ React.createElement(DkPunkPicker, { T, onPick: pickPunk, onClose: () => setPicking(false) })), !st.loading && st.registered && /* @__PURE__ */ React.createElement(Card, { label: T.linksCard || "Links" }, SOCIALS.map((s, i) => socialRow(s, i === SOCIALS.length - 1))));
4146
+ ), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--dim)", flexShrink: 0 } }, ".beagles.eth"), /* @__PURE__ */ React.createElement(Btn, { tone: "accent", size: "sm", disabled: busy || !label.trim(), onClick: register }, busy ? "\u2026" : st.registered && st.mineOwned ? T.ensUpdate || "update" : T.ensRegister || "register")), st.registered && !st.mineOwned && /* @__PURE__ */ React.createElement("div", { style: { padding: "10px 16px", borderBottom: "1px solid var(--line)", fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--faint)" } }, (T.ensWalletOwned || "registered via your mobile wallet as") + " ", /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)" } }, rec && rec.name)), st.registered && /* @__PURE__ */ React.createElement("div", { className: "dk-row dk-wrap", style: row }, /* @__PURE__ */ React.createElement("span", { className: "dk-lbl", style: { width: 130, flexShrink: 0, fontFamily: "var(--mono)", fontSize: 11.5, fontWeight: 600, color: "var(--faint)", textTransform: "uppercase", letterSpacing: 0.5 } }, T.ensAvatar || "avatar"), upAvatar ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("img", { src: upAvatar, alt: "", width: 34, height: 34, style: { width: 34, height: 34, borderRadius: 9, objectFit: "cover", flexShrink: 0, background: "var(--panel-2)" } }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--text)" } }, T.ensCustom || "custom image"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !canWrite, onClick: () => post("/api/ens-avatar", { nftid: null }) }, T.ensClear || "clear")) : punkId != null ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DkPunkAvatar, { id: punkId, size: 34, radius: 9, fallbackSeed: me.userId }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--text)" } }, `CryptoPunk #${punkId}`), /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !canWrite, onClick: () => post("/api/ens-avatar", { nftid: null }) }, T.ensClear || "clear")) : /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)" } }, T.ensNotSet || "not set"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !canWrite, onClick: () => fileRef.current && fileRef.current.click() }, T.ensUpload || "upload"), /* @__PURE__ */ React.createElement(Btn, { size: "sm", disabled: busy || !canWrite, onClick: () => setPicking(true) }, T.ensChoose || "choose"), /* @__PURE__ */ React.createElement("input", { ref: fileRef, type: "file", accept: "image/*", onChange: onUploadFile, style: { display: "none" } })), walletRow(T.ensEth || "ethereum", "eth", boundEth, bindEth, false), walletRow(T.ensSol || "solana", "sol", boundSol, bindSol, !msg && !st.writeBlocked), st.writeBlocked && hostWalletBind && /* @__PURE__ */ React.createElement("div", { style: { padding: "10px 16px", borderBottom: msg ? "1px solid var(--line)" : "none", fontFamily: "var(--ui)", fontSize: 12, lineHeight: 1.5, color: "var(--faint)" } }, st.writeBlocked), msg && /* @__PURE__ */ React.createElement("div", { style: { padding: "10px 16px", fontFamily: "var(--ui)", fontSize: 12, color: msg.tone === "ok" ? "var(--online)" : "var(--danger)" } }, msg.text)), picking && /* @__PURE__ */ React.createElement(DkPunkPicker, { T, onPick: pickPunk, onClose: () => setPicking(false) })), !st.loading && st.registered && /* @__PURE__ */ React.createElement(Card, { label: T.linksCard || "Links" }, SOCIALS.map((s, i) => socialRow(s, i === SOCIALS.length - 1))));
4058
4147
  }
4059
4148
  function DkUpdateCheck({ T }) {
4060
4149
  const [st, setSt] = React.useState("idle");
@@ -6454,6 +6543,10 @@ var STR = {
6454
6543
  ensUnbind: "unbind",
6455
6544
  ensNotBound: "not bound",
6456
6545
  ensWebOnly: "Registering a beagles.eth name needs the Beagle phone or desktop app. Your avatar can be set above without one.",
6546
+ ensProven: "signed by this wallet",
6547
+ ensUnproven: "no wallet signature yet",
6548
+ ensProve: "sign",
6549
+ ensSigning: "approve the signature in your wallet\u2026",
6457
6550
  ensAvatar: "avatar",
6458
6551
  ensChoose: "choose",
6459
6552
  ensClear: "clear",
@@ -6649,6 +6742,10 @@ var STR = {
6649
6742
  ensUnbind: "\u89E3\u7ED1",
6650
6743
  ensNotBound: "\u672A\u7ED1\u5B9A",
6651
6744
  ensWebOnly: "\u6CE8\u518C beagles.eth \u540D\u5B57\u9700\u8981 Beagle \u624B\u673A\u6216\u684C\u9762\u5E94\u7528\u3002\u5934\u50CF\u4E0D\u9700\u8981\u540D\u5B57\uFF0C\u53EF\u5728\u4E0A\u65B9\u76F4\u63A5\u8BBE\u7F6E\u3002",
6745
+ ensProven: "\u5DF2\u7531\u8BE5\u94B1\u5305\u7B7E\u540D",
6746
+ ensUnproven: "\u5C1A\u65E0\u94B1\u5305\u7B7E\u540D",
6747
+ ensProve: "\u7B7E\u540D",
6748
+ ensSigning: "\u8BF7\u5728\u94B1\u5305\u4E2D\u786E\u8BA4\u7B7E\u540D\u2026",
6652
6749
  ensAvatar: "\u5934\u50CF",
6653
6750
  ensChoose: "\u9009\u62E9",
6654
6751
  ensClear: "\u6E05\u9664",
@@ -6983,10 +7080,10 @@ function DkApp() {
6983
7080
  window.location.reload();
6984
7081
  }, [me && me.beagleVer]);
6985
7082
  const [assistantTs, setAssistantTs] = React.useState(loadAssistantTs);
6986
- const peers = React.useMemo(() => (data.peers || []).concat([Object.assign({}, ASSISTANT_PEER, {
7083
+ const peers = React.useMemo(() => (data.peers || []).concat(hostAssistant ? [Object.assign({}, ASSISTANT_PEER, {
6987
7084
  lastTs: assistantTs,
6988
7085
  lastTime: assistantTs ? dkClock(assistantTs) : ""
6989
- })]), [data.peers, assistantTs]);
7086
+ })] : []), [data.peers, assistantTs]);
6990
7087
  const [pendingAddr, setPendingAddr] = React.useState(() => dkParseDeepLinkAddress());
6991
7088
  React.useEffect(() => {
6992
7089
  const onHash = () => {
@@ -0,0 +1,38 @@
1
+ // Types for @decentnetwork/beagle-ui/wallet-proof. Copied to dist/ by the build.
2
+ export type WalletChain = 'eth' | 'sol';
3
+ export interface WalletProof { message: string; signature: string }
4
+ export type VerifyResult = { ok: true; issued: string } | { ok: false; error: string };
5
+
6
+ export const WALLET_PROOF_VERSION: 1;
7
+ export const WALLET_CHAINS: WalletChain[];
8
+ export const COIN_TYPE: { eth: '60'; sol: '501' };
9
+ export const PROOF_KEY: { eth: 'proof.eth'; sol: 'proof.sol' };
10
+
11
+ export function isWalletAddress(chain: string, address: unknown): boolean;
12
+ export function proofTime(date?: Date): string;
13
+ export function walletProofMessage(f: { userid: string; chain: WalletChain; address: string; issued: string }): string;
14
+ export function parseWalletProofMessage(msg: unknown): { userid: string; chain: WalletChain; address: string; issued: string } | null;
15
+ export function encodeWalletProof(p: WalletProof): string;
16
+ export function decodeWalletProof(text: unknown): WalletProof | null;
17
+ export function base58Encode(bytes: Uint8Array): string;
18
+ export function base58Decode(str: string): Uint8Array;
19
+ export function signEthProof(provider: { request(a: { method: string; params: unknown[] }): Promise<unknown> }, f: { userid: string; address: string; issued?: string }): Promise<WalletProof>;
20
+ export function signSolProof(wallet: { signMessage(m: Uint8Array, display?: string): Promise<unknown> }, f: { userid: string; address: string; issued?: string }): Promise<WalletProof>;
21
+
22
+ export function eip191Hash(message: string): Uint8Array;
23
+ export function recoverEthAddress(message: string, signature: string): string;
24
+ export function verifyWalletProof(a: {
25
+ chain: string; address: string; userid: string;
26
+ proof: string | WalletProof | null | undefined;
27
+ maxAgeMs?: number; now?: number;
28
+ }): VerifyResult;
29
+ export function verifyRecordWallet(
30
+ record: { owner?: string; addresses?: Record<string, string>; texts?: Record<string, string> } | null | undefined,
31
+ chain: WalletChain
32
+ ): { ok: true; address: string; via: 'proof'; issued: string } | { ok: true; address: string; via: 'owner' } | { ok: false; error: string };
33
+
34
+ export const BIND_MAX_AGE_MS: number;
35
+ export function applyWalletBind(
36
+ record: { addresses?: Record<string, string>; texts?: Record<string, string> } | null | undefined,
37
+ req: { chain: string; address: unknown; proof?: string | WalletProof | null; userid: string; now?: number }
38
+ ): { ok: true; addresses: Record<string, string>; texts: Record<string, string> } | { ok: false; status: number; error: string };