argus/src/registrar-config.js
Local Dev 53428a605f bns: one network table instead of chipnet constants copied into fifteen places
Every client hard-coded the chipnet beacons, address prefix and electrum
servers on its own: resolver, registrar config, wallets, gateway, indexer,
mirror scripts, the browser bundle and the mobile Java. A mainnet launch would
have meant finding all of them and hoping none was missed.

The table now lives in resolver-web.js, the one file every client already
shares, so it stays a single-file drop-in. BNS_NETWORK selects the record;
unset means chipnet, so nothing changes today: the live index resolves the
same 60 names and 20 TLDs, the 67 offline tests pass, and the dashboard,
market and studio load the same values through BNS.NETWORK.

The mainnet record carries the verified public servers, the prefix and its
own Sia bucket, but its beacons, start height and operator address are
deliberately null: requireBeacons() refuses to scan until they are pinned in
the order ROADMAP-MAINNET.md §6 requires. Bns.java reads a generated
BnsNetwork.java so the phone cannot drift from the desktop clients.
NETWORK-CONFIG.md records what reads the table and what a launch still pins.
2026-09-27 21:18:37 +02:00

107 lines
4.9 KiB
JavaScript

// Registrar configuration — the only place prices, the fee address and the
// network live. Everything here is public information; no secrets (SECURITY.md
// rule 0). This file is bundled into the public register page as-is.
import { ELECTRUM } from "./lib/electrum.js";
import { NETWORK } from "./lib/resolver-web.js";
export const REGISTRAR = {
// ---- network -----------------------------------------------------------
// Everything chain-specific is read from the network table in
// lib/resolver-web.js (selected by BNS_NETWORK; chipnet unless set). Mainnet
// stays a deliberate switch: its beacons are unpinned until
// ROADMAP-MAINNET.md §6 is done, and requireBeacons() refuses before that.
network: NETWORK.id,
addressPrefix: NETWORK.prefix,
explorerTx: NETWORK.explorerTx,
// ---- chain connectivity -------------------------------------------------
// Order matters: the pool connects in parallel but prefers earlier entries,
// and takes the UNION of beacon history across everything that answers.
//
// PUT YOUR OWN FULCRUM FIRST, ON 443. Every public server below listens on a
// non-standard port (50004 / 62002), so a single firewall or VPN rule blocks
// all of them at once — observed 2026-07-27, when all three went TCP-closed
// while :443 stayed reachable. A self-hosted server on 443 is worth more than
// three more public ones, and it is not a centralisation trade: the public
// servers stay in the list as cross-checks, and the union means your own
// server cannot hide a registration from you either.
//
// selfHosted: "wss://bns.example.st:443",
//
// See OPERATIONS-electrum.md for running one.
selfHosted: null,
get electrum() {
return this.selfHosted ? [this.selfHosted, ...ELECTRUM] : ELECTRUM;
},
// ---- discovery ---------------------------------------------------------
beacon: NETWORK.beacon,
tldBeacon: NETWORK.tldBeacon,
// ---- price -------------------------------------------------------------
// The service fee is a plain output inside the buyer's own minting
// transaction — the registrar model, like ENS. We never take custody: the
// certificate is minted straight to the buyer's key in the same transaction
// that pays us. If `sats` is 0n the output is omitted entirely.
serviceFee: {
address: NETWORK.operatorAddress,
sats: 10_000n, // chipnet placeholder price — set real pricing at mainnet
},
// Miner fee rate, satoshis per byte. 1 sat/byte confirms fine on BCH.
feeRate: 1,
// ---- external wallets ---------------------------------------------------
// WizardConnect only. WalletConnect v2 was removed deliberately (2026-07-27):
// it needs a Reown project ID and a relay we do not control, and WizardConnect
// already covers the same wallets (Cashonize 0.9+, Paytaca) over an
// end-to-end-encrypted Nostr transport with no third-party project registration.
//
// CashConnect is NOT here on purpose either — see the note in
// Decentralized.DNS/REGISTRATION-STATUS.md. Its V0 model has the wallet build
// the transaction and auto-select its own inputs, which cannot mint a name
// certificate: a CashTokens genesis is defined by one specific outpoint the
// dApp must choose, and the category ID is that outpoint's txid.
wizardConnect: {
dappName: "Silent Mode",
dappIcon: "https://silentmode.st/icon.png",
},
// Faucets shown to a buyer whose new wallet is empty (empty on mainnet).
faucets: NETWORK.faucets,
};
/** Human-readable price breakdown for the confirmation screen. */
export function priceSummary(costs) {
const bch = (sats) => (Number(sats) / 1e8).toFixed(8).replace(/0+$/, "").replace(/\.$/, "");
const rows = [];
// Split fee: show TLD-owner + platform lines when the TLD-owner cut is
// non-zero (i.e. buyer is registering under a TLD they don't own).
const tldOwner = BigInt(costs.tldOwnerFeeSats ?? 0n);
const platform = BigInt(costs.serviceFeeSats ?? 0n);
if (tldOwner > 0n) {
rows.push({
label: "Service fee — TLD owner (90%)",
sats: tldOwner, bch: bch(tldOwner),
note: "goes to whoever holds this TLD's certificate on chain",
});
rows.push({
label: "Service fee — platform (10%)",
sats: platform, bch: bch(platform),
note: "covers the resolver + gateway + indexer",
});
} else {
rows.push({ label: "Service fee", sats: platform, bch: bch(platform) });
}
rows.push({ label: "Network fee", sats: costs.chainFeeSats, bch: bch(costs.chainFeeSats) });
rows.push({ label: "Discovery beacon", sats: costs.beaconDustSats, bch: bch(costs.beaconDustSats) });
rows.push({
label: "Held in the certificate",
sats: costs.certificateDustSats,
bch: bch(costs.certificateDustSats),
note: "stays yours — it lives in the certificate you now own",
});
rows.push({ label: "Total", sats: costs.netCostSats, bch: bch(costs.netCostSats), total: true });
return rows;
}