// Wiring for the sign-in and registration screens. // // This file contains no cryptography — wallet.js does all of that. What it // does is decide which of the three ways to sign is on offer and get out of // the way of the other two: // // 1. A wallet the browser already has (Theseus exposes one). Best case: the // key never comes near this page. // 2. A recovery phrase typed here, used once, and wiped. // 3. A signature produced somewhere else entirely and pasted in. This one // works with JavaScript switched off, which is why the markup ships with // the paste box present and the buttons hidden until this script decides // they are usable. // // The progressive-enhancement direction matters: buttons start hidden and are // revealed, rather than starting visible and being disabled. A script that // fails to load leaves a page that still works. (() => { "use strict"; const CONFIG = window.SIRIUS_PRESS || {}; const strings = CONFIG.strings || {}; function setStatus(block, text, kind = "") { const el = block.querySelector(".sirius-wallet__status"); if (!el) return; el.textContent = text || ""; el.className = "sirius-wallet__status" + (kind ? " is-" + kind : ""); } /** Submit the form the block sits in, once a signature is in place. */ function submitForm(block) { const form = block.closest("form"); if (!form) return; // WordPress marks its username and password inputs `required`, and the // wallet path deliberately leaves both empty — the signature is the // credential. Constraint validation would otherwise refuse the submit and // point a "Please fill out this field" bubble at an input the visitor is // not supposed to touch, with the signature already sitting in the form. // // Only reached from the wallet buttons, so password sign-in keeps its // validation. form.noValidate = true; if (typeof form.requestSubmit === "function") { form.requestSubmit(); } else { form.submit(); } } function setSignature(block, signature, address) { const field = block.querySelector(".sirius-wallet__signature"); if (field) field.value = signature; // The address the signature claims. The server compares it against the // one it recovers, which is the only way a signature over the wrong text // becomes an error instead of a different identity. const claim = block.querySelector(".sirius-wallet__address"); if (claim && address) claim.value = address; } /** On the paste path the visitor supplies the address themselves. */ function adoptTypedAddress(block) { const typed = block.querySelector(".sirius-wallet__address-input"); const claim = block.querySelector(".sirius-wallet__address"); if (typed && claim && typed.value.trim() && !claim.value) { claim.value = typed.value.trim(); } } async function signWithPhrase(block) { const input = block.querySelector(".sirius-wallet__phrase-input"); const message = block.querySelector(".sirius-wallet__message").value; const phrase = input ? input.value : ""; const check = window.SiriusWallet.validatePhrase(phrase); if (!check.ok) { setStatus(block, check.error, "error"); return; } setStatus(block, strings.signing || "Signing…"); let wallet; try { wallet = await window.SiriusWallet.fromPhrase(phrase, { prefix: CONFIG.prefix, path: CONFIG.path, }); setSignature(block, await wallet.sign(message), wallet.address); } catch (err) { setStatus(block, err.message || String(err), "error"); return; } finally { // The phrase has done its one job. Clear it from the field and from the // wallet object before anything else on the page runs. if (input) input.value = ""; if (wallet && wallet.forget) wallet.forget(); } submitForm(block); } async function signWithExternal(block) { const external = window.SiriusWallet.external(); if (!external) { setStatus(block, strings.noWallet || "No wallet found in this browser.", "error"); return; } const message = block.querySelector(".sirius-wallet__message").value; setStatus(block, strings.signing || "Signing…"); try { setSignature(block, await external.sign(message), await external.address()); } catch (err) { // A user declining the wallet's approval dialog is a normal outcome, not // an error worth shouting about. const text = err && err.message ? err.message : String(err); setStatus(block, /reject/i.test(text) ? "" : text, /reject/i.test(text) ? "" : "error"); return; } submitForm(block); } function enhance(block) { // Whatever happens next, a hand-typed address must reach the server. const form = block.closest("form"); if (form) form.addEventListener("submit", () => adoptTypedAddress(block)); const signButton = block.querySelector(".sirius-wallet__sign"); const externalButton = block.querySelector(".sirius-wallet__external"); const phraseArea = block.querySelector(".sirius-wallet__phrase"); if (!window.SiriusWallet || !window.crypto || !window.crypto.subtle) { // No usable crypto — for instance an http:// origin, where WebCrypto is // unavailable. Say so, and leave the paste path alone: it still works. setStatus( block, "This browser cannot sign here (a secure https connection is required). Paste a signature from your own wallet instead.", "error", ); const manual = block.querySelector(".sirius-wallet__manual"); if (manual) manual.open = true; return; } if (phraseArea) phraseArea.hidden = false; if (signButton) { signButton.hidden = false; signButton.addEventListener("click", () => signWithPhrase(block)); } if (externalButton && window.SiriusWallet.external()) { externalButton.hidden = false; externalButton.addEventListener("click", () => signWithExternal(block)); } // Enter in the phrase box should sign, not submit an unsigned form. const input = block.querySelector(".sirius-wallet__phrase-input"); if (input) { input.addEventListener("keydown", (event) => { if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); signWithPhrase(block); } }); } } /** The "which address is this phrase?" helper on the recovery page. */ function enhanceDerive() { const button = document.getElementById("sirius_derive_go"); const input = document.getElementById("sirius_derive_phrase"); const out = document.querySelector(".sirius-derive__out"); if (!button || !input || !out) return; button.addEventListener("click", async () => { const check = window.SiriusWallet.validatePhrase(input.value); if (!check.ok) { out.textContent = check.error; out.className = "sirius-derive__out is-error"; return; } out.textContent = strings.signing || "Working…"; out.className = "sirius-derive__out"; try { const wallet = await window.SiriusWallet.fromPhrase(input.value, { prefix: CONFIG.prefix, path: CONFIG.path, }); out.textContent = wallet.address; out.className = "sirius-derive__out is-address"; wallet.forget(); } catch (err) { out.textContent = err.message || String(err); out.className = "sirius-derive__out is-error"; } finally { input.value = ""; } }); } function start() { document.querySelectorAll(".sirius-wallet").forEach(enhance); enhanceDerive(); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", start); } else { start(); } })();