argus/test/registry-lifecycle.test.mjs
Local Dev f9a5eb367f Theseus navigator UI overhaul, BNS-only indexer, site restructure
Theseus Navigator:
- Firefox-style toolbar: SVG back/fwd/reload/home buttons
- security padlock badge in the address bar opens a floating site-info
  overlay VIEW (on top of the page, never pushes content down)
- Edge-style favorites bar (shown only on the new-tab/home page)
- search-engine picker (DuckDuckGo/Google/Bing/Brave/Startpage)
- Theseus logo moved right, opens Settings; native menu removed
- address bar doubles as search; bottom resolver status line

Argus:
- BNS-only indexer: an electrum endpoint over a bare BCHN node
  (watches just the beacon, caches txs so no txindex needed) + test
- registry lifecycle / portfolio / name-index modules

Site restructure (nav/apps/store/hermes/choose) + runbook notes.
2026-07-30 08:16:55 +02:00

261 lines
11 KiB
JavaScript
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.

// Lifecycle, pricing and the payment split.
//
// These are the rules the covenant will encode, so this file doubles as the
// fixture the CashScript contract must reproduce exactly. Several tests assert
// economic properties rather than arithmetic — no surplus cliff, the owner always
// holds the cheap option, the split never loses a satoshi — because those are the
// ones that quietly stop being true when a constant gets edited.
import test from "node:test";
import assert from "node:assert/strict";
import {
PHASE, phaseAt, expiryFor, idleEndFor,
BLOCKS_PER_YEAR, GRACE_BLOCKS, GRACE_MONTHS,
TIERS, tierForName, tierConfig,
renewalPriceSats, idlePremiumSats, openFloorSats,
takeoverPriceSats, reclaimPriceSats,
SPLIT, splitTakeover,
COMMIT, commitIsMature,
FUND, describeLifecycle,
IDLE_PREMIUM_MULTIPLE, OPEN_FLOOR_MULTIPLE,
} from "../src/lib/registry-lifecycle.js";
const START = 900_000;
const EXPIRY = expiryFor(START, 1);
const IDLE_END = idleEndFor(EXPIRY);
const TIER = 3; // the tier the design's $20 / $1000 / $100 figures describe
// ---------- phases ----------
test("phase boundaries are exact, not approximate", () => {
assert.equal(phaseAt({ height: EXPIRY - 1, expiryHeight: EXPIRY, idleEndHeight: IDLE_END }), PHASE.active);
assert.equal(phaseAt({ height: EXPIRY, expiryHeight: EXPIRY, idleEndHeight: IDLE_END }), PHASE.idle);
assert.equal(phaseAt({ height: IDLE_END - 1, expiryHeight: EXPIRY, idleEndHeight: IDLE_END }), PHASE.idle);
assert.equal(phaseAt({ height: IDLE_END, expiryHeight: EXPIRY, idleEndHeight: IDLE_END }), PHASE.open);
});
test("grace is 6 months, not 12 — the namespace has to actually recycle", () => {
assert.equal(GRACE_MONTHS, 6);
assert.equal(IDLE_END - EXPIRY, GRACE_BLOCKS);
// A 1-year name is unavailable for 18 months after last use, not 24.
assert.equal(IDLE_END - START, BLOCKS_PER_YEAR + GRACE_BLOCKS);
assert.ok(IDLE_END - START < BLOCKS_PER_YEAR * 2);
});
// ---------- tiers ----------
test("tier follows label length and ignores the TLD", () => {
assert.equal(tierForName("ab"), 1);
assert.equal(tierForName("abcd"), 2);
assert.equal(tierForName("abcdefg"), 3);
assert.equal(tierForName("abcdefgh"), 4);
assert.equal(tierForName("sirius.nav"), 3, "the TLD must not shift the tier");
assert.equal(tierForName("sirius"), tierForName("sirius.nav"));
});
test("tier 3 lands on the design's $20 / $1000 / $100 figures", () => {
assert.equal(renewalPriceSats(TIER), 200_000n);
assert.equal(idlePremiumSats(TIER), 10_000_000n); // 50x
assert.equal(openFloorSats(TIER), 1_000_000n); // 5x
assert.equal(IDLE_PREMIUM_MULTIPLE, 50n);
assert.equal(OPEN_FLOOR_MULTIPLE, 5n);
});
test("shorter names cost more, monotonically", () => {
const prices = TIERS.map((t) => t.renewalSats);
for (let i = 1; i < prices.length; i++) {
assert.ok(prices[i] < prices[i - 1], `tier ${i + 1} must be cheaper than tier ${i}`);
}
});
test("multi-year renewal is a straight multiple", () => {
assert.equal(renewalPriceSats(TIER, 3), renewalPriceSats(TIER) * 3n);
});
// ---------- the decay curve ----------
const priceAt = (height) => takeoverPriceSats({ height, expiryHeight: EXPIRY, idleEndHeight: IDLE_END, tier: TIER });
test("an ACTIVE name is not for sale at any price", () => {
assert.equal(priceAt(EXPIRY - 1), null);
assert.equal(priceAt(START), null);
});
test("takeover starts at the premium and ends at the floor", () => {
assert.equal(priceAt(EXPIRY), idlePremiumSats(TIER));
assert.equal(priceAt(IDLE_END), openFloorSats(TIER));
assert.equal(priceAt(IDLE_END + 100_000), openFloorSats(TIER), "the floor holds indefinitely");
});
test("the price only ever falls — never rises, never plateaus mid-glide", () => {
let previous = priceAt(EXPIRY);
for (let h = EXPIRY + 100; h < IDLE_END; h += 500) {
const p = priceAt(h);
assert.ok(p < previous, `price rose or stalled at height ${h}`);
previous = p;
}
});
test("NO SURPLUS CLIFF at the IDLE→OPEN boundary", () => {
// The whole reason for a glide instead of a step. If the price fell sharply at
// one block, every bidder would submit in that block and the winner would be
// whoever paid the biggest miner fee — the outcome the pricing exists to avoid.
const justBefore = priceAt(IDLE_END - 1);
const atBoundary = priceAt(IDLE_END);
const gap = justBefore - atBoundary;
const floor = openFloorSats(TIER);
assert.ok(gap >= 0n);
assert.ok(
gap * 1000n < floor,
`boundary drop of ${gap} sat against a ${floor} sat floor is a cliff, not a glide`,
);
});
test("the glide is roughly linear across the window", () => {
const mid = priceAt(EXPIRY + Number(GRACE_BLOCKS) / 2);
const expected = (idlePremiumSats(TIER) + openFloorSats(TIER)) / 2n;
const drift = mid > expected ? mid - expected : expected - mid;
assert.ok(drift * 100n < expected, "midpoint should sit near the average of the endpoints");
});
test("price is never below the floor at any height", () => {
const floor = openFloorSats(TIER);
for (let h = EXPIRY; h < IDLE_END + 10_000; h += 977) {
assert.ok(priceAt(h) >= floor, `price dipped under the floor at ${h}`);
}
});
// ---------- the owner's advantage ----------
test("the owner's option is tens of times cheaper throughout IDLE", () => {
// This asymmetry — not the length of the grace window — is what protects a
// present owner. Renewing always beats being bought out.
const reclaim = reclaimPriceSats({ tier: TIER });
for (const h of [EXPIRY, EXPIRY + 1000, IDLE_END - 1]) {
assert.ok(priceAt(h) > reclaim * 4n, `takeover at ${h} is not meaningfully dearer than reclaiming`);
}
assert.equal(idlePremiumSats(TIER) / reclaim, 50n, "50x at the start of IDLE");
});
test("reclaim price ignores phase and demand entirely", () => {
// An owner must never be priceable out of a name they already hold.
assert.equal(reclaimPriceSats({ tier: TIER }), renewalPriceSats(TIER));
assert.equal(reclaimPriceSats({ tier: TIER, years: 2 }), renewalPriceSats(TIER, 2));
});
// ---------- the split ----------
test("IDLE takeover splits 30 / 0 / 70", () => {
const s = splitTakeover({ amountSats: 10_000_000n, phase: PHASE.idle });
assert.equal(s.ownerSats, 3_000_000n);
assert.equal(s.minerFeeSats, 0n);
assert.equal(s.fundSats, 7_000_000n);
});
test("OPEN takeover pays the absent owner nothing", () => {
// The 30% is a buy-out while they are still entitled to reclaim. Once OPEN,
// that entitlement has lapsed and so has the justification.
const s = splitTakeover({ amountSats: 1_000_000n, phase: PHASE.open });
assert.equal(s.ownerSats, 0n);
assert.equal(s.fundSats, 1_000_000n);
});
test("the split never loses or conjures a satoshi, at any amount", () => {
// Integer division truncates; the fund takes the remainder so the parts always
// reconcile. A covenant that is one satoshi off simply will not unlock.
for (const amount of [1n, 3n, 7n, 99n, 101n, 999_999n, 10_000_001n, 123_456_789n]) {
for (const phase of [PHASE.idle, PHASE.open]) {
const s = splitTakeover({ amountSats: amount, phase });
assert.equal(
s.ownerSats + s.minerFeeSats + s.fundSats, amount,
`${amount} sat in ${phase} did not reconcile`,
);
assert.ok(s.ownerSats >= 0n && s.fundSats >= 0n && s.minerFeeSats >= 0n);
}
}
});
test("no miner share by default", () => {
assert.equal(SPLIT.minerPercent, 0n, "15% is a discount for the one party that also orders blocks");
});
test("restoring the miner share still reconciles exactly", () => {
// Proving the 30/15/55 variant is a one-value change, not a rewrite.
const original = SPLIT.minerPercent;
try {
SPLIT.minerPercent = 15n;
const s = splitTakeover({ amountSats: 10_000_000n, phase: PHASE.idle });
assert.equal(s.ownerSats, 3_000_000n);
assert.equal(s.minerFeeSats, 1_500_000n);
assert.equal(s.fundSats, 5_500_000n);
assert.equal(s.ownerSats + s.minerFeeSats + s.fundSats, 10_000_000n);
} finally {
SPLIT.minerPercent = original;
}
});
test("selling an ACTIVE name is refused outright", () => {
assert.throws(() => splitTakeover({ amountSats: 1000n, phase: PHASE.active }), /not for sale/);
});
test("a zero or negative takeover is refused", () => {
assert.throws(() => splitTakeover({ amountSats: 0n, phase: PHASE.idle }), /must be positive/);
});
// ---------- commit–reveal ----------
test("commit maturity window is one confirmation to one week", () => {
assert.equal(COMMIT.minConfirmations, 1);
assert.equal(COMMIT.maxConfirmations, 1008);
assert.equal(COMMIT.saltBytes, 16);
assert.equal(commitIsMature(1000, 1000), false, "same block is not a confirmation");
assert.equal(commitIsMature(1000, 1001), true, "one confirmation is enough to fix ordering");
assert.equal(commitIsMature(1000, 2008), true);
assert.equal(commitIsMature(1000, 2009), false, "stale commits must not be hoardable");
});
// ---------- fund ----------
test("the fund is unset until deliberately configured", () => {
// A covenant bakes this in permanently. Shipping a placeholder would send
// every takeover to whatever address happened to be in the file.
assert.equal(FUND.address, null);
assert.equal(FUND.multisig, "2-of-3");
assert.equal(FUND.voluntary, true, "the answer to the IFP comparison rests on this");
});
// ---------- the UI summary ----------
test("describeLifecycle gives a UI everything it needs to warn in time", () => {
const active = describeLifecycle({ height: EXPIRY - 5000, expiryHeight: EXPIRY, idleEndHeight: IDLE_END, tier: TIER });
assert.equal(active.phase, PHASE.active);
assert.equal(active.blocksUntilExpiry, 5000);
assert.equal(active.takeoverPriceSats, null);
assert.equal(active.ownerCanReclaim, true);
const idle = describeLifecycle({ height: EXPIRY + 10, expiryHeight: EXPIRY, idleEndHeight: IDLE_END, tier: TIER });
assert.equal(idle.phase, PHASE.idle);
assert.equal(idle.blocksUntilExpiry, 0);
assert.ok(idle.blocksUntilOpen > 0);
assert.ok(idle.ownerAdvantage >= 40, `owner advantage of ${idle.ownerAdvantage}x is too thin`);
const open = describeLifecycle({ height: IDLE_END + 1, expiryHeight: EXPIRY, idleEndHeight: IDLE_END, tier: TIER });
assert.equal(open.phase, PHASE.open);
assert.equal(open.ownerCanReclaim, false);
assert.equal(open.takeoverPriceSats, openFloorSats(TIER));
});
test("every tier survives a full lifecycle without a cliff or a rise", () => {
for (const { tier } of TIERS) {
const floor = openFloorSats(tier);
let previous = takeoverPriceSats({ height: EXPIRY, expiryHeight: EXPIRY, idleEndHeight: IDLE_END, tier });
assert.equal(previous, idlePremiumSats(tier));
for (let h = EXPIRY + 1000; h <= IDLE_END; h += 2000) {
const p = takeoverPriceSats({ height: h, expiryHeight: EXPIRY, idleEndHeight: IDLE_END, tier });
assert.ok(p <= previous, `tier ${tier} price rose at ${h}`);
assert.ok(p >= floor, `tier ${tier} dipped below floor at ${h}`);
previous = p;
}
}
});