sirius/tld.html
Local Dev 94bf3bcf68 feat(sirius-x): merge register.html into landing via shared mint flow
Register.html and the landing were running duplicate name-search UIs.
The register one was reported broken; the landing one already works
inline. Merged both into the landing:

- New js/register-flow.js — the full mint modal + wallet setup (Create /
  Import / Unlock / WizardConnect / Fund / Confirm / Register / Point
  it somewhere) extracted from register.html's <script type='module'>
  and turned into a shared module. Injects its own scoped modal HTML +
  CSS into the host page on load and exposes
  window.siriusRegisterName(fullName) as its public entry point.

- Landing search results now have Register buttons that call the shared
  flow directly. The whole 'search -> pick name -> mint' journey stays
  on one page, no redirects, no duplicate search UI to maintain.

- register.html reduced to a redirect stub: preserves ?q= if present,
  refreshes/JS-forwards to './' (landing), and shows a one-line
  'Name search moved to the home page' fallback for JS-off users.
  Old links keep working; no /register.html deep links are broken.

- All nav 'Register' entries now point at './#search-input' (or
  '../#search-input' on subpages), scrolling straight to the landing
  search box. Footer injector and every internal href updated the
  same way. Zero live href='.../register.html' left in the tree.

Verified: register.html?q=hello redirects to /?q=hello; landing
search 'fresh42abc' -> Register button -> modal opens with
'Register fresh42abc.bch' at the wallet-choice step.
2026-09-07 22:36:02 +02:00

573 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Register a TLD — Sirius.X</title>
<meta name="description" content="Mint a top-level domain on Bitcoin Cash. Any wallet can register a TLD; second-level names can then be sold under it.">
<link rel="icon" href="./assets/favicon.svg" type="image/svg+xml">
<link rel="mask-icon" href="./assets/favicon.svg" color="#d6ff3d">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Sirius.X">
<meta property="og:title" content="Register a TLD on Bitcoin Cash">
<meta property="og:description" content="Mint a TLD certificate. One BCH transaction, no renewal, no registrar.">
<meta property="og:url" content="https://silentmode.st/sirius-x/tld.html">
<meta property="og:image" content="https://silentmode.st/sirius-x/brand/banner.svg">
<style>
:root{
--bg:#0b0e14; --panel:#141a24; --panel2:#18202c; --line:rgba(255,255,255,.09);
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d;
--ok:#4fd1a5; --taken:#f6768a; --warn:#ffc75f;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:radial-gradient(1100px 560px at 50% -12%,#16202e,var(--bg));
color:var(--ink);font:16px/1.65 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;min-height:100vh}
a{color:var(--acid)}
.wrap{max-width:820px;margin:0 auto;padding:0 1.2rem}
.topnav{position:sticky;top:0;z-index:20;display:flex;gap:2px;align-items:center;flex-wrap:wrap;
padding:10px 1.2rem;background:rgba(11,14,20,.82);backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
.topnav .brand{font-weight:600;color:var(--ink);text-decoration:none;margin-right:14px;font-size:15px;letter-spacing:.2px}
.topnav .brand .g{color:var(--acid)}
.topnav a{color:var(--mut);text-decoration:none;padding:6px 12px;border-radius:8px;font-size:14px}
.topnav a:hover{color:var(--ink);background:var(--panel)}
.topnav a.here{color:var(--acid);background:rgba(214,255,61,.08)}
.topnav a.portal{margin-left:auto;background:rgba(214,255,61,.10);color:var(--acid);font-weight:500}
.topnav a.portal:hover{background:rgba(214,255,61,.18)}
header.hero{text-align:center;padding:3.4rem 1.2rem 1rem}
.mark{font-size:44px;line-height:1}
h1{font-size:clamp(1.7rem,4.5vw,2.3rem);margin:.3rem 0 .3rem}
h1 .g{color:var(--acid)}
.tag{color:var(--mut);font-size:1rem;max-width:600px;margin:0 auto}
h2{font-size:1.25rem;margin:0 0 .4rem}
h3{font-size:1rem;margin:0 0 .35rem}
section{padding:2rem 0;border-top:1px solid var(--line);margin-top:2rem}
section:first-of-type{border-top:none;margin-top:1rem}
.card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:20px 22px;margin-top:1rem}
label{display:block;font-size:13px;color:var(--mut);margin:14px 0 5px}
.field-hint{color:var(--dim);font-size:11.5px;margin-left:8px}
textarea,input[type=text],input[type=password]{
width:100%;padding:11px 13px;border-radius:10px;border:1px solid #ffffff22;background:#0e131b;
color:var(--ink);font-size:14px;font-family:inherit;outline:none
}
textarea{min-height:74px;resize:vertical;font-family:ui-monospace,monospace;font-size:13px}
textarea:focus,input:focus{border-color:#4b7bec}
.btn{display:inline-block;text-decoration:none;padding:10px 18px;border-radius:10px;background:#4b7bec;color:#fff;font-size:14px;border:none;cursor:pointer;font-family:inherit}
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--ink)}
.btn.acid{background:var(--acid);color:#0b0e14;font-weight:600}
.btn.small{padding:6px 12px;font-size:12.5px}
.btn:hover{filter:brightness(1.12)}
.btn:disabled{opacity:.45;cursor:not-allowed;filter:none}
.row{display:flex;gap:10px;flex-wrap:wrap;margin-top:16px;align-items:center}
.muted{color:var(--mut);font-size:13px}
.dim{color:var(--dim);font-size:12.5px}
code,.mono{background:#0e131b;border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-family:ui-monospace,monospace;font-size:13px;word-break:break-all}
.badge{display:inline-block;font-size:11px;padding:2px 10px;border-radius:999px;font-weight:600}
.b-ok{background:rgba(79,209,165,.15);color:var(--ok)}
.b-taken{background:rgba(246,118,138,.15);color:var(--taken)}
.b-check{background:rgba(139,152,169,.14);color:var(--mut)}
.status{background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:10px 14px;font-size:13px;color:var(--mut);margin-top:12px}
.status.ok{border-left:3px solid var(--ok)}
.status.err{border-left:3px solid var(--taken);color:var(--taken)}
.status.warn{border-left:3px solid var(--warn);color:var(--warn)}
.hidden{display:none}
.err{color:var(--taken);font-size:13.5px;margin-top:10px}
.authbox{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}
.authbox .card{margin:0}
.authbox h3{margin:0 0 .3rem;font-size:1rem}
.authbox p{margin:0;color:var(--mut);font-size:13px}
/* TLD search cell */
.search{max-width:600px;margin:1.5rem auto 0}
.searchrow{display:flex;gap:8px}
.searchrow input{flex:1;padding:14px 18px;font-size:17px;border-radius:12px;border:1px solid #ffffff22;background:#141a24;color:var(--ink)}
.searchrow input:focus{border-color:#4b7bec;outline:none}
.searchhint{font-size:12.5px;color:var(--mut);margin-top:8px;text-align:center}
.result-card{display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:center;
background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 18px;margin-top:12px}
.result-card .lbl{font-family:ui-monospace,monospace;font-size:18px;color:var(--ink)}
.result-card .lbl .dot{color:var(--acid)}
/* TLD registry table */
table.tlds{width:100%;border-collapse:collapse;margin-top:1rem;font-size:13px}
table.tlds th,table.tlds td{padding:8px 12px;text-align:left;border-bottom:1px solid var(--line);vertical-align:top}
table.tlds th{color:var(--mut);font-weight:500;font-size:11.5px;text-transform:uppercase;letter-spacing:.4px}
table.tlds .tld{font-family:ui-monospace,monospace;color:var(--acid);font-size:13.5px;font-weight:600}
table.tlds .cat{font-family:ui-monospace,monospace;color:var(--mut);font-size:11.5px;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Registration progress */
.steps-log{margin-top:12px;font-family:ui-monospace,monospace;font-size:12px;color:var(--mut);
background:#0e131b;border:1px solid var(--line);border-radius:8px;padding:10px 12px;max-height:140px;overflow-y:auto;display:none}
.steps-log.on{display:block}
.steps-log div::before{content:"› ";color:var(--acid)}
.msg{font-size:13px;margin-top:12px}
.msg.ok{color:var(--ok)} .msg.err{color:var(--taken)}
footer{border-top:1px solid var(--line);padding:2rem 0 3rem;color:var(--dim);font-size:13px;text-align:center;margin-top:2.5rem}
</style>
</head>
<body>
<nav class="topnav">
<a class="brand" href="./">⭐ <span class="g">Sirius.X</span></a>
<a href="./#try">Try it</a>
<a href="./#search-input">🪪 Register</a>
<a href="./tld.html" class="here">🌐 TLD</a>
<a href="./theseus/"><svg width="14" height="14" viewBox="0 0 64 64" style="vertical-align:-2px;margin-right:4px" aria-hidden="true"><rect width="64" height="64" rx="12" fill="#0b0e14"/><path d="M32 6 L38 32 L26 32 Z" fill="#e04e4e"/><path d="M38 32 L32 58 L26 32 Z" fill="#d6ff3d"/><circle cx="32" cy="32" r="3" fill="#0b0e14"/></svg>Theseus</a>
<a href="./portal.html" class="portal">🔑 Sign in</a>
</nav>
<header class="hero">
<div class="mark">🌐</div>
<h1>Register a <span class="g">TLD</span></h1>
<p class="tag">A TLD is a certificate on the Bitcoin Cash chain. You mint it, you own it — any
second-level name registered under it lives in your TLD's registry. One BCH transaction,
no renewal, no ICANN.</p>
</header>
<div class="wrap">
<section id="search-section">
<h2>Search a TLD</h2>
<p class="muted">Type a label (a–z, 0–9, hyphen; up to 16 chars). We check the on-chain TLD
registry live and, if the label is free, show a register button.</p>
<div class="search">
<div class="searchrow">
<input id="q" placeholder="your-tld" spellcheck="false" autocomplete="off" autofocus maxlength="16">
</div>
<div class="searchhint">Available TLDs appear with a register button. Taken labels show which category holds them.</div>
</div>
<div id="result"></div>
</section>
<section id="signin-section" class="hidden">
<h2>Sign in to mint</h2>
<p class="muted">The TLD certificate lands in the token address of the wallet that signs the mint —
that wallet becomes the registry operator.</p>
<div class="authbox" style="margin-top:14px">
<div class="card" id="card-new">
<h3>🆕 New wallet</h3>
<p>Generate a fresh 12-word phrase in this tab. Nothing is saved — write it down before continuing.</p>
<div class="row">
<button class="btn acid" id="new-btn">Generate a phrase</button>
</div>
<div id="new-phrase" class="hidden" style="margin-top:14px">
<label>Your new recovery phrase</label>
<textarea id="new-seed" readonly></textarea>
<label style="margin-top:10px;display:flex;gap:9px;align-items:flex-start;font-size:13.5px">
<input type="checkbox" id="new-ack" style="margin-top:3px">
<span>I have written this phrase down. There is no reset if I lose it.</span>
</label>
<div class="row">
<button class="btn acid" id="new-signin-btn" disabled>Continue →</button>
<button class="btn ghost" id="new-copy-btn">Copy phrase</button>
</div>
</div>
<div id="new-err" class="err hidden"></div>
</div>
<div class="card" id="card-import">
<h3>📥 Import a seed phrase</h3>
<p>Paste your 12- or 24-word phrase. It stays in memory only.</p>
<label for="seed">Recovery phrase</label>
<textarea id="seed" placeholder="word1 word2 … word12" spellcheck="false" autocomplete="off"></textarea>
<div class="row">
<button class="btn acid" id="signin-btn">Continue →</button>
</div>
<div id="signin-err" class="err hidden"></div>
</div>
<div class="card" id="card-wc">
<h3>🔗 WizardConnect</h3>
<p>Cashonize 0.9+ or Paytaca. The wallet signs the mint on your device.</p>
<div class="row">
<button class="btn acid" id="wc-btn">Connect wallet</button>
</div>
<div id="wc-uri-box" class="hidden" style="margin-top:14px">
<label>Paste this into your WC-capable wallet:</label>
<div style="display:flex;gap:8px;align-items:center;margin-top:6px">
<code id="wc-uri" style="flex:1;font-size:12px"></code>
<button class="btn ghost small" id="wc-copy" type="button">Copy</button>
</div>
<p class="dim" style="margin-top:8px">Waiting for your wallet…</p>
<div class="row"><button class="btn ghost small" id="wc-cancel" type="button">Cancel</button></div>
</div>
<div id="wc-err" class="err hidden"></div>
</div>
</div>
</section>
<section id="mint-section" class="hidden">
<h2>Mint <span id="mint-label" class="mono" style="color:var(--acid)">—</span></h2>
<div class="card">
<div class="status" id="mint-wallet">Signed in as <code id="mint-addr">—</code></div>
<label>Service fee (USD) <span class="field-hint">chipnet placeholder rate; mainnet will use an oracle</span></label>
<input type="text" id="mint-usd" value="5" style="max-width:200px">
<div class="status" id="mint-price">= <b>0</b> sats · rate <b>0</b> sats/USD</div>
<div class="row">
<button class="btn acid" id="mint-submit" disabled>Register</button>
<button class="btn ghost small" id="mint-back">← Back to search</button>
<button class="btn ghost small" id="mint-signout">Sign out</button>
</div>
<div class="steps-log" id="mint-log"></div>
<div id="mint-msg" class="msg"></div>
</div>
</section>
<section id="listing">
<h2>Registered TLDs</h2>
<p class="muted">Fetched from the on-chain TLD registry via the public gateway. Refresh the page for the latest.</p>
<div id="tlds-status" class="status">Loading…</div>
<table class="tlds" id="tlds-table" style="display:none">
<thead><tr><th>TLD</th><th>Category</th></tr></thead>
<tbody></tbody>
</table>
</section>
</div>
<footer id="site-footer"></footer>
<script type="importmap">
{ "imports": { "@bitauth/libauth": "https://silentmode.st/js/libauth.js" } }
</script>
<script type="module">
import * as BNS from "https://silentmode.st/js/bns-register.js?v=20260907tldext";
const $ = (id) => document.getElementById(id);
const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[c]));
// ---------- oracle stub ----------
// Same chipnet-only static rate as the portal. Mainnet swaps this for a real
// price feed; the sats output is what actually gets locked into the mint tx.
const CHIPNET_SATS_PER_USD = 250_000; // ~$400/BCH test rate
const usdToSats = (u) => {
const n = Number(String(u).replace(/[^\d.]/g, "")) || 0;
return Math.max(0, Math.round(n * CHIPNET_SATS_PER_USD));
};
// ---------- state ----------
let wallet = null; // built-in wallet or WC wrapper
let candidateLabel = null; // TLD label the user chose to register
// ---------- TLD list (live) ----------
async function loadTldList() {
const status = $("tlds-status"), tbody = $("tlds-table").querySelector("tbody");
try {
const r = await fetch("https://navigate.st/api/tlds");
if (!r.ok) throw new Error("api " + r.status);
const j = await r.json();
const tlds = (j.tlds || []).map((t) => (typeof t === "string" ? { tld: t } : t));
tlds.sort((a, b) => a.tld.localeCompare(b.tld));
tbody.innerHTML = tlds.map((t) =>
`<tr><td class="tld">.${esc(t.tld)}</td><td class="cat">${esc(t.certificate || t.category || "")}</td></tr>`
).join("");
$("tlds-table").style.display = "";
status.className = "status ok";
status.textContent = `${tlds.length} TLDs registered on chipnet.`;
return tlds.map((t) => t.tld);
} catch (e) {
status.className = "status err";
status.textContent = "Could not load TLD list: " + (e.message || e);
return null;
}
}
let registeredTlds = null;
loadTldList().then((list) => { registeredTlds = list; refreshSearch(); });
// ---------- search + result ----------
function validLabel(s) { return /^[a-z0-9-]{1,16}$/.test(s); }
function normLabel(s) { return String(s).toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0, 16); }
function renderResult(label, state) {
const res = $("result");
if (!label) { res.innerHTML = ""; return; }
if (state === "checking") {
res.innerHTML = `<div class="result-card">
<div class="lbl"><span class="dot">.</span>${esc(label)}</div>
<span class="badge b-check">checking…</span>
<span></span>
</div>`;
return;
}
if (state === "taken") {
res.innerHTML = `<div class="result-card">
<div class="lbl"><span class="dot">.</span>${esc(label)}</div>
<span class="badge b-taken">taken</span>
<span></span>
</div>
<p class="dim" style="margin-top:8px">Someone already minted <code>.${esc(label)}</code>. Try a different label — TLDs are first-come, first-served.</p>`;
return;
}
// available
res.innerHTML = `<div class="result-card">
<div class="lbl"><span class="dot">.</span>${esc(label)}</div>
<span class="badge b-ok">available</span>
<button class="btn acid" id="go-register">Register .${esc(label)} →</button>
</div>`;
$("go-register").onclick = () => beginRegister(label);
}
let currentLabel = "";
function refreshSearch() {
const raw = normLabel($("q").value);
currentLabel = raw;
if (!raw) { renderResult("", null); return; }
if (!validLabel(raw)) { renderResult(raw, "invalid"); return; }
// If we already have the registry snapshot, decide locally — no API round-trip.
if (registeredTlds !== null) {
renderResult(raw, registeredTlds.includes(raw) ? "taken" : "ok");
return;
}
renderResult(raw, "checking");
}
$("q").addEventListener("input", () => {
$("q").value = normLabel($("q").value);
refreshSearch();
});
$("q").addEventListener("keydown", (e) => {
if (e.key === "Enter" && currentLabel && registeredTlds && !registeredTlds.includes(currentLabel)) {
beginRegister(currentLabel);
}
});
// ---------- flow: search → wallet → mint ----------
function beginRegister(label) {
candidateLabel = label;
$("mint-label").textContent = "." + label;
// Show the sign-in section if not signed in; otherwise straight to mint.
if (!wallet) {
$("signin-section").classList.remove("hidden");
$("signin-section").scrollIntoView({ behavior: "smooth", block: "start" });
} else {
showMintSection();
}
}
function showMintSection() {
$("signin-section").classList.add("hidden");
$("mint-section").classList.remove("hidden");
$("mint-addr").textContent = wallet.address;
updateMintPrice();
updateMintButton();
$("mint-section").scrollIntoView({ behavior: "smooth", block: "start" });
}
function backToSearch() {
$("mint-section").classList.add("hidden");
$("search-section").scrollIntoView({ behavior: "smooth", block: "start" });
}
$("mint-back").addEventListener("click", backToSearch);
// ---------- wallet: import ----------
$("signin-btn").addEventListener("click", async () => {
const raw = $("seed").value.trim();
const errBox = $("signin-err");
errBox.classList.add("hidden");
if (!raw) { errBox.textContent = "Paste your seed phrase to continue."; errBox.classList.remove("hidden"); return; }
const btn = $("signin-btn");
btn.disabled = true; btn.textContent = "Unlocking…";
try { wallet = await BNS.BuiltInWallet.fromMnemonic(raw); }
catch (e) {
errBox.textContent = "Could not unlock: " + (e.message || e);
errBox.classList.remove("hidden");
btn.disabled = false; btn.textContent = "Continue →";
return;
}
$("seed").value = "";
btn.disabled = false; btn.textContent = "Continue →";
writeProfile(wallet);
showMintSection();
});
// ---------- wallet: new ----------
$("new-btn").addEventListener("click", async () => {
const btn = $("new-btn"), errBox = $("new-err");
errBox.classList.add("hidden");
btn.disabled = true; btn.textContent = "Generating…";
try {
const fresh = await BNS.BuiltInWallet.create();
wallet = fresh;
$("new-seed").value = fresh.mnemonic;
$("new-phrase").classList.remove("hidden");
btn.textContent = "Phrase generated";
} catch (e) {
errBox.textContent = "Could not generate: " + (e.message || e);
errBox.classList.remove("hidden");
btn.disabled = false; btn.textContent = "Generate a phrase";
}
});
$("new-ack").addEventListener("change", (e) => { $("new-signin-btn").disabled = !e.target.checked; });
$("new-copy-btn").addEventListener("click", () => navigator.clipboard?.writeText($("new-seed").value));
$("new-signin-btn").addEventListener("click", () => {
if (!wallet) return;
$("new-seed").value = "";
$("new-phrase").classList.add("hidden");
writeProfile(wallet);
showMintSection();
});
// ---------- wallet: WC ----------
$("wc-btn").addEventListener("click", async () => {
const btn = $("wc-btn"), errBox = $("wc-err"), uriBox = $("wc-uri-box");
errBox.classList.add("hidden");
btn.disabled = true; btn.textContent = "Starting…";
let session;
try {
const mod = await import("https://silentmode.st/js/wizardconnect.js");
session = await mod.openWizardSession({
dappName: BNS.REGISTRAR.wizardConnect.dappName,
dappIcon: BNS.REGISTRAR.wizardConnect.dappIcon,
prefix: BNS.REGISTRAR.addressPrefix,
crypto: {
pubkeyToAddress: BNS.pubkeyToAddress,
pubkeyToLockingBytecode: BNS.pubkeyToLockingBytecode,
binToHex: BNS.binToHex,
txidOfHex: BNS.txidOfHex,
},
});
} catch (e) {
errBox.textContent = "Could not start WC: " + (e.message || e);
errBox.classList.remove("hidden");
btn.disabled = false; btn.textContent = "Connect wallet";
return;
}
$("wc-uri").textContent = session.qrUri ?? session.uri;
uriBox.classList.remove("hidden");
btn.textContent = "Waiting for wallet…";
try { await session.ready; }
catch (e) {
errBox.textContent = "Wallet did not approve: " + (e.message || e);
errBox.classList.remove("hidden");
uriBox.classList.add("hidden");
btn.disabled = false; btn.textContent = "Connect wallet";
try { await session.disconnect(); } catch {}
return;
}
const addrs = await session.getAddresses();
if (!addrs?.length) {
errBox.textContent = "Wallet returned no addresses.";
errBox.classList.remove("hidden");
uriBox.classList.add("hidden");
btn.disabled = false; btn.textContent = "Connect wallet";
try { await session.disconnect(); } catch {}
return;
}
wallet = {
source: "wc",
address: addrs[0],
tokenAddress: BNS.toTokenAddress(addrs[0]),
watchedAddresses: addrs,
session,
};
writeProfile(wallet);
uriBox.classList.add("hidden");
btn.disabled = false; btn.textContent = "Connect wallet";
showMintSection();
});
$("wc-cancel").addEventListener("click", async () => {
if (wallet?.session) { try { await wallet.session.disconnect(); } catch {} }
$("wc-uri-box").classList.add("hidden");
$("wc-btn").disabled = false; $("wc-btn").textContent = "Connect wallet";
});
$("wc-copy").addEventListener("click", () => navigator.clipboard?.writeText($("wc-uri").textContent));
// ---------- profile persistence (mirrors portal.html) ----------
function writeProfile(w) {
try {
localStorage.setItem("siriusProfile", JSON.stringify({
address: w.address, tokenAddress: w.tokenAddress,
source: w.source ?? "seed", signedInAt: Date.now(),
}));
} catch {}
window.dispatchEvent(new Event("siriusProfileChanged"));
}
function clearProfile() {
try { localStorage.removeItem("siriusProfile"); } catch {}
window.dispatchEvent(new Event("siriusProfileChanged"));
}
$("mint-signout").addEventListener("click", async () => {
if (wallet?.source === "wc" && wallet.session) {
try { await wallet.session.disconnect(); } catch {}
}
wallet = null;
clearProfile();
$("mint-section").classList.add("hidden");
$("signin-section").classList.remove("hidden");
});
// ---------- mint ----------
function updateMintPrice() {
const usd = $("mint-usd").value.trim() || "0";
const sats = usdToSats(usd);
$("mint-price").innerHTML = `= <b>${sats.toLocaleString()}</b> sats · rate <b>${CHIPNET_SATS_PER_USD.toLocaleString()}</b> sats/USD (chipnet)`;
$("mint-submit").textContent = `Register .${candidateLabel} (~${sats.toLocaleString()} sats)`;
}
function updateMintButton() {
$("mint-submit").disabled = !wallet || !candidateLabel;
}
$("mint-usd").addEventListener("input", updateMintPrice);
$("mint-submit").addEventListener("click", async () => {
if (!wallet || !candidateLabel) return;
const sats = BigInt(usdToSats($("mint-usd").value.trim() || "0"));
const msg = $("mint-msg"); msg.textContent = ""; msg.className = "msg";
const log = $("mint-log"); log.className = "steps-log on"; log.innerHTML = "";
const push = (t) => { const d = document.createElement("div"); d.textContent = t; log.appendChild(d); log.scrollTop = log.scrollHeight; };
$("mint-submit").disabled = true;
$("mint-submit").textContent = "Signing…";
let el = null;
try {
el = await BNS.connect();
push("connected to chipnet");
const serviceFee = { address: BNS.REGISTRAR?.serviceFee?.address ?? null, sats };
const res = wallet.source === "wc"
? await BNS.registerTldWithExternalWallet(el, {
session: wallet.session, tld: candidateLabel, records: {},
serviceFee, tldListUrl: "https://navigate.st/api/tlds",
onProgress: push,
})
: await BNS.registerTldWithBuiltInWallet(el, {
wallet, tld: candidateLabel, records: {},
serviceFee, tldListUrl: "https://navigate.st/api/tlds",
onProgress: push,
});
push("broadcast: " + (res.txid || "(no txid)"));
msg.className = "msg ok";
msg.innerHTML = `✅ <b>.${esc(candidateLabel)}</b> minted. Category <code>${esc((res.category || "").slice(0, 16))}…</code><br>
The certificate now lives in your token address; names can be registered under it from
<a href="./#search-input">the name search</a>.`;
$("mint-submit").textContent = "Registered ✓";
// Refresh the live TLD table so the new one shows up.
setTimeout(() => loadTldList().then((l) => { registeredTlds = l; refreshSearch(); }), 3500);
} catch (e) {
msg.className = "msg err";
const raw = String(e.message || e);
if (/wallet is empty/i.test(raw)) {
msg.innerHTML = `Failed: wallet is empty. Fund <code>${esc(wallet.address)}</code> with chipnet coins first — free from
<a href="https://tbch.googol.cash/" target="_blank" rel="noopener">tbch.googol.cash</a> or
<a href="https://chipnet.bch.ninja/faucet" target="_blank" rel="noopener">chipnet.bch.ninja/faucet</a>.`;
} else {
msg.textContent = "Failed: " + raw;
}
push("error: " + raw);
$("mint-submit").disabled = false;
updateMintPrice();
} finally {
try { el?.close?.(); } catch {}
}
});
// ---------- ?q= deep-link (from landing search too, if someone comes with a label) ----------
(() => {
const q = normLabel(new URLSearchParams(location.search).get("q") || "");
if (!q) return;
$("q").value = q;
// wait for registeredTlds to load
const wait = setInterval(() => {
if (registeredTlds === null) return;
clearInterval(wait);
refreshSearch();
setTimeout(() => $("q").scrollIntoView({ behavior: "smooth", block: "start" }), 100);
}, 150);
})();
</script>
<script defer src="./js/site-footer.js?v=20260907rel"></script>
<script defer src="./js/profile-menu.js?v=20260907wallet"></script>
</body>
</html>