2026-09-09 01:09:05 +02:00
// Sirius.X pricing model — deterministic per-label cost so users can predict
// what a name or TLD will cost before they click Register. Public tiers only;
// operator-level discounts and coupon overrides land in a future revision
// (the plan is oracle-fed rates + coupon-code redemption at mint time).
//
feat(sirius-x): TLD owner panel — owner-set price, on/off switch, private TLDs
A TLD owner needed to run their namespace without the operator: set what
every name under the TLD sells for, take the TLD off the public registry
for a while, and still register names under it themselves.
Both settings live on chain in the TLD's TUPD records (`price`, `hidden`)
because changes are rare and every client already walks the TLD beacon.
The gateway's /api/tlds now carries records, owner, price_usd and
hidden_by per TLD; pricing.js quotes the owner price ahead of the length
tiers and only trusts a TLD_BEACON-sourced list; the register flow refuses
hidden and frozen TLDs for the public but lets the owner through at the
platform share only (the 90% owner cut would be paid to themselves). The
portal's TLDs tab loads real holdings, shows price and on/off state, and
gives each TLD a one-click switch, a price/policy editor and an inline
"register a name under .tld" form. Docs and the design table describe the
two new records.
2026-09-16 21:28:18 +02:00
// Two layers decide what a NAME costs:
// 1. The TLD owner's on-chain policy (`price`, `hidden`, `policy`) — read
// from the gateway's /api/tlds snapshot of the TLD beacon. A TLD owner
// who sets `price: 7` sells every name under that TLD for $7, flat.
// 2. Otherwise the length tiers below.
//
2026-09-09 01:09:05 +02:00
// Exposed as window.siriusPricing so both the register-flow modal and the
// inline search widgets on landing/tld.html use one source of truth.
( ( ) => {
// Chipnet placeholder rate: mainnet swaps this for a real BCH/USD oracle.
// The numbers keep the demo transactions in the "few chipnet coins" range
// so a single faucet visit covers a name or a TLD.
feat(sirius-x): prices in BCH, amounts in bits, live BCH/USD rate from several exchanges
Every dollar figure on the site came from a hard-coded 250,000 sats per
dollar, which implies $400 per BCH; the market is near $250, so name
prices, TLD fees and sale listings were shown about 60% too cheap in
dollars. The gateway now serves /api/price: the median of Coinbase,
Kraken, Bitstamp, Binance and CoinGecko public tickers, no keys, cached
60 s, last good answer kept if every source fails. The site reads it
first, queries the same tickers itself if the gateway is unreachable,
remembers the last rate per browser, and only falls back to a constant
for the very first paint. Pages repaint when the rate arrives, and the
rate line says where it came from and how old it is.
Units: satoshi no longer appear anywhere. Sale prices, the seller's
input and the buy dialog are in BCH with the dollar figure beside
them; balances, fees and dust are in bits (1 bit = 100 satoshi).
2026-09-20 18:18:43 +02:00
// BCH/USD rate. Live: the gateway's /api/price (median of several exchanges)
// or, if that is unreachable, the same public tickers straight from the
// browser; the last good rate is remembered per browser; the constant is
// only the very first paint before anything answers. Chipnet coins have no
// market, so the mainnet rate is what makes "$7 per name" mean something.
const FALLBACK _SATS _PER _USD = 400_000 ;
let SATS _PER _USD = FALLBACK _SATS _PER _USD ;
let priceMeta = { usd : null , at : 0 , sources : 0 , origin : "fallback" } ;
try { const c = JSON . parse ( localStorage . getItem ( "siriusPrice" ) || "null" ) ; if ( c && c . usd > 0 ) { SATS _PER _USD = Math . round ( 1e8 / c . usd ) ; priceMeta = { ... c , origin : "cached" } ; } } catch { }
const usdToSats = ( usd ) => BigInt ( Math . max ( 0 , Math . round ( Number ( usd ) * SATS _PER _USD ) ) ) ;
const PRICE _API = "https://silentmode.st/api/price" ;
const BROWSER _SOURCES = [
[ "coinbase" , "https://api.coinbase.com/v2/prices/BCH-USD/spot" , ( j ) => Number ( j ? . data ? . amount ) ] ,
[ "kraken" , "https://api.kraken.com/0/public/Ticker?pair=BCHUSD" , ( j ) => Number ( Object . values ( j ? . result || { } ) [ 0 ] ? . c ? . [ 0 ] ) ] ,
[ "binance" , "https://api.binance.com/api/v3/ticker/price?symbol=BCHUSDT" , ( j ) => Number ( j ? . price ) ] ,
[ "coingecko" , "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin-cash&vs_currencies=usd" , ( j ) => Number ( j ? . [ "bitcoin-cash" ] ? . usd ) ] ,
] ;
function applyPrice ( usd , sources , origin ) {
if ( ! ( usd > 0 ) ) return false ;
SATS _PER _USD = Math . round ( 1e8 / usd ) ;
priceMeta = { usd , at : Date . now ( ) , sources , origin } ;
try { localStorage . setItem ( "siriusPrice" , JSON . stringify ( priceMeta ) ) ; } catch { }
try { window . dispatchEvent ( new CustomEvent ( "sirius:price" , { detail : priceMeta } ) ) ; } catch { }
return true ;
}
let priceLoad = null ;
function loadPrice ( { force = false } = { } ) {
if ( priceLoad && ! force ) return priceLoad ;
priceLoad = ( async ( ) => {
try {
const r = await fetch ( PRICE _API , { cache : "no-store" , signal : AbortSignal . timeout ( 6000 ) } ) ;
const j = await r . json ( ) ;
if ( j && j . usd > 0 && ! j . stale ) return applyPrice ( j . usd , j . median _of || 0 , "gateway" ) ;
} catch { }
const got = ( await Promise . allSettled ( BROWSER _SOURCES . map ( async ( [ name , url , pick ] ) => {
const r = await fetch ( url , { signal : AbortSignal . timeout ( 6000 ) } ) ; if ( ! r . ok ) throw new Error ( String ( r . status ) ) ;
const v = pick ( await r . json ( ) ) ; if ( ! ( v > 0 ) ) throw new Error ( "no price" ) ; return v ;
} ) ) ) . filter ( ( x ) => x . status === "fulfilled" ) . map ( ( x ) => x . value ) . sort ( ( a , b ) => a - b ) ;
if ( got . length ) { const m = Math . floor ( got . length / 2 ) ; return applyPrice ( got . length % 2 ? got [ m ] : ( got [ m - 1 ] + got [ m ] ) / 2 , got . length , "exchanges" ) ; }
return false ;
} ) ( ) ;
return priceLoad ;
}
const priceInfo = ( ) => priceMeta . usd ? ` ${ priceMeta . origin === "gateway" || priceMeta . origin === "exchanges" ? ` median of ${ priceMeta . sources } exchange ${ priceMeta . sources === 1 ? "" : "s" } ` : priceMeta . origin === "cached" ? "last known rate" : "fallback rate" } ${ priceMeta . at ? ", " + Math . max ( 0 , Math . round ( ( Date . now ( ) - priceMeta . at ) / 60000 ) ) + " min ago" : "" } ` : "fallback rate" ;
loadPrice ( ) ;
setInterval ( ( ) => loadPrice ( { force : true } ) , 5 * 60 * 1000 ) ;
2026-09-09 01:09:05 +02:00
const cleanLabel = ( s ) => String ( s || "" ) . toLowerCase ( ) . replace ( /[^a-z0-9-]/g , "" ) ;
feat(sirius-x): TLD owner panel — owner-set price, on/off switch, private TLDs
A TLD owner needed to run their namespace without the operator: set what
every name under the TLD sells for, take the TLD off the public registry
for a while, and still register names under it themselves.
Both settings live on chain in the TLD's TUPD records (`price`, `hidden`)
because changes are rare and every client already walks the TLD beacon.
The gateway's /api/tlds now carries records, owner, price_usd and
hidden_by per TLD; pricing.js quotes the owner price ahead of the length
tiers and only trusts a TLD_BEACON-sourced list; the register flow refuses
hidden and frozen TLDs for the public but lets the owner through at the
platform share only (the 90% owner cut would be paid to themselves). The
portal's TLDs tab loads real holdings, shows price and on/off state, and
gives each TLD a one-click switch, a price/policy editor and an inline
"register a name under .tld" form. Docs and the design table describe the
two new records.
2026-09-16 21:28:18 +02:00
// ---------- TLD owner policies ----------
// tld -> { price, hidden, frozen, policy, owner, records }
// Filled from /api/tlds (see loadTldPolicies). Unknown TLDs fall through
// to tier pricing and are treated as sellable, so a gateway blip never
// blocks a purchase; the chain is the authority at mint time anyway.
// include_hidden=1 so the policy map knows about switched-off (private)
// TLDs too — their owners register names under them from the portal, and
// the register flow must be able to price and gate them. The public
// listing (visibleTlds) still drops anything hidden.
const TLD _API = "https://navigate.st/api/tlds?include_hidden=1" ;
const TLD _POLICIES = new Map ( ) ;
let tldList = null ; // raw /api/tlds rows, once loaded (hidden ones excluded)
let tldLoad = null ; // memoised in-flight fetch
function normalisePolicy ( row ) {
const rec = row && row . records && typeof row . records === "object" ? row . records : { } ;
const priceRaw = row && row . price _usd != null ? row . price _usd : rec . price ;
const price = typeof priceRaw === "number" && Number . isFinite ( priceRaw ) && priceRaw >= 0 ? priceRaw : null ;
const hidden = ! ! ( row && row . hidden ) || rec . hidden === 1 || rec . hidden === true ;
const policy = typeof rec . policy === "string" ? rec . policy : "open" ;
return {
tld : row . tld ,
price ,
hidden ,
hiddenBy : row && row . hidden _by ? row . hidden _by : ( hidden ? "owner" : null ) ,
frozen : policy === "frozen" ,
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
// cosign: anyone may register, but the TLD owner must co-sign the
// registration (indexers reject a REG under a cosign/hidden TLD that
// does not carry the TLD certificate). hidden implies the same rule
// on chain; in the UI hidden is owner-only, cosign is request-based.
cosign : policy === "cosign" ,
feat(sirius-x): TLD owner panel — owner-set price, on/off switch, private TLDs
A TLD owner needed to run their namespace without the operator: set what
every name under the TLD sells for, take the TLD off the public registry
for a while, and still register names under it themselves.
Both settings live on chain in the TLD's TUPD records (`price`, `hidden`)
because changes are rare and every client already walks the TLD beacon.
The gateway's /api/tlds now carries records, owner, price_usd and
hidden_by per TLD; pricing.js quotes the owner price ahead of the length
tiers and only trusts a TLD_BEACON-sourced list; the register flow refuses
hidden and frozen TLDs for the public but lets the owner through at the
platform share only (the 90% owner cut would be paid to themselves). The
portal's TLDs tab loads real holdings, shows price and on/off state, and
gives each TLD a one-click switch, a price/policy editor and an inline
"register a name under .tld" form. Docs and the design table describe the
two new records.
2026-09-16 21:28:18 +02:00
policy ,
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
category : row && row . category ? row . category : null ,
feat(sirius-x): TLD owner panel — owner-set price, on/off switch, private TLDs
A TLD owner needed to run their namespace without the operator: set what
every name under the TLD sells for, take the TLD off the public registry
for a while, and still register names under it themselves.
Both settings live on chain in the TLD's TUPD records (`price`, `hidden`)
because changes are rare and every client already walks the TLD beacon.
The gateway's /api/tlds now carries records, owner, price_usd and
hidden_by per TLD; pricing.js quotes the owner price ahead of the length
tiers and only trusts a TLD_BEACON-sourced list; the register flow refuses
hidden and frozen TLDs for the public but lets the owner through at the
platform share only (the 90% owner cut would be paid to themselves). The
portal's TLDs tab loads real holdings, shows price and on/off state, and
gives each TLD a one-click switch, a price/policy editor and an inline
"register a name under .tld" form. Docs and the design table describe the
two new records.
2026-09-16 21:28:18 +02:00
owner : row && row . owner ? row . owner : null ,
records : rec ,
} ;
}
function setTldPolicies ( rows ) {
TLD _POLICIES . clear ( ) ;
for ( const r of rows || [ ] ) {
const row = typeof r === "string" ? { tld : r } : r ;
const tld = cleanLabel ( row && row . tld ) ;
if ( ! tld ) continue ;
TLD _POLICIES . set ( tld , normalisePolicy ( { ... row , tld } ) ) ;
}
}
// Fetch the TLD list once per page. Resolves to the array of visible TLD
// rows (or null on failure) — never rejects, callers just fall back.
function loadTldPolicies ( { url = TLD _API , force = false } = { } ) {
if ( tldLoad && ! force ) return tldLoad ;
tldLoad = ( async ( ) => {
try {
const r = await fetch ( url , { cache : "no-store" } ) ;
if ( ! r . ok ) throw new Error ( "api " + r . status ) ;
const j = await r . json ( ) ;
const rows = Array . isArray ( j . tlds ) ? j . tlds : [ ] ;
setTldPolicies ( rows ) ;
// Only a beacon-sourced list is a sellable list. A gateway still
// serving the legacy `tlds.bch`-derived union includes every suffix
// that ever appeared on a name (".de", ".silentmode", …), which is
// not what the registry sells — keep the page's own fallback then.
tldList = j . source === "TLD_BEACON" ? rows . filter ( ( t ) => ! t . hidden ) : null ;
return tldList ;
} catch {
return null ;
}
} ) ( ) ;
return tldLoad ;
}
// Resolves once the list is loaded, or after `ms` — search UIs await this
// so the first paint already shows owner prices without ever hanging.
function tldReady ( ms = 2500 ) {
return Promise . race ( [
loadTldPolicies ( ) ,
new Promise ( ( res ) => setTimeout ( ( ) => res ( null ) , ms ) ) ,
] ) ;
}
function tldPolicy ( tld ) {
return TLD _POLICIES . get ( cleanLabel ( tld ) ) || null ;
}
// Does one of `addresses` (the signed-in wallet's) hold this TLD's NFT?
// `owner` comes from the gateway's beacon snapshot (mint output, updated
// on every TUPD carrier), so a wallet that owns the TLD matches here.
function isTldOwner ( tld , addresses ) {
const p = tldPolicy ( tld ) ;
if ( ! p || ! p . owner || ! addresses ) return false ;
const list = Array . isArray ( addresses ) ? addresses : [ addresses ] ;
return list . some ( ( a ) => typeof a === "string" && a === p . owner ) ;
}
// Can names be sold under this TLD right now?
// hidden → owner switched the TLD off: private TLD. The public cannot
// buy, but the OWNER still can — pass their wallet addresses
// and a hidden TLD they hold answers ok with owner: true.
// frozen → owner set policy "frozen" (no new registrations, owner included)
function tldSellable ( tld , addresses ) {
const p = tldPolicy ( tld ) ;
if ( ! p ) return { ok : true , reason : null , owner : false } ;
const owner = isTldOwner ( tld , addresses ) ;
if ( p . frozen ) return { ok : false , reason : "frozen" , policy : p , owner } ;
if ( p . hidden && ! owner ) return { ok : false , reason : "hidden" , policy : p , owner } ;
feat(bns): name marketplace and TLD-owner co-sign rule
Two gaps the owner panel left open. First, a hidden TLD was only a UI
gate: anyone could still broadcast a REG under it and every indexer
admitted it. Second, there was no way to sell a name without trusting
the other side.
Co-sign rule (consensus, applied in lockstep by bns.js and
resolver-web.js): a REG under a TLD whose records at that height say
policy "cosign" or hidden 1 is indexed only if the transaction carries
the TLD's own certificate. The certificate can only be spent by the
owner's key and is re-issued to them in the same transaction, so it is
a co-signature nobody can forge and nothing is consumed. The TLD map
now keeps the TUPD timeline so policy is evaluated at the REG height.
Owners register under their private TLDs with the certificate added
from their own wallet; third parties under a "cosign" TLD build the
full transaction, sign their inputs and queue it at /api/cosign, where
the owner approves it from the dashboard (signCosignRequest refuses to
sign unless the certificate returns to the same locking script).
Marketplace: a listing is the seller's certificate input plus a price
output signed SIGHASH_SINGLE|ANYONECANPAY, stored by the gateway as a
bulletin board (/api/market, verified against the on-chain owner and
pruned when the certificate moves). The buyer completes it in one
transaction, so the seller is paid exactly when the name moves.
Cancelling also spends the certificate once so the offer is void.
Site: market.html, Sell sub-tab and Pending approvals in the portal,
Market link in nav and footer, six dictionaries extended, cache tags
bumped. Verified on chipnet: cosigned.sc registered by a throwaway
wallet through the queue with the .sc certificate back at the owner;
aloevera.test listed and delisted through the API.
Ariadne's resolver-web.js copy and the mobile Bns.java port still need
the co-sign rule; until then they admit REGs this index rejects.
2026-09-17 04:04:22 +02:00
// Sellable, but the registration needs the owner's co-signature: the
// flow builds the request and parks it in the gateway's approval queue.
if ( p . cosign && ! owner ) return { ok : true , reason : "cosign" , policy : p , owner } ;
feat(sirius-x): TLD owner panel — owner-set price, on/off switch, private TLDs
A TLD owner needed to run their namespace without the operator: set what
every name under the TLD sells for, take the TLD off the public registry
for a while, and still register names under it themselves.
Both settings live on chain in the TLD's TUPD records (`price`, `hidden`)
because changes are rare and every client already walks the TLD beacon.
The gateway's /api/tlds now carries records, owner, price_usd and
hidden_by per TLD; pricing.js quotes the owner price ahead of the length
tiers and only trusts a TLD_BEACON-sourced list; the register flow refuses
hidden and frozen TLDs for the public but lets the owner through at the
platform share only (the 90% owner cut would be paid to themselves). The
portal's TLDs tab loads real holdings, shows price and on/off state, and
gives each TLD a one-click switch, a price/policy editor and an inline
"register a name under .tld" form. Docs and the design table describe the
two new records.
2026-09-16 21:28:18 +02:00
return { ok : true , reason : null , policy : p , owner } ;
}
// Visible TLD labels in listing order, or null if no beacon-sourced list
// is available (callers keep their own fallback list then).
function visibleTlds ( ) {
return tldList ? tldList . map ( ( t ) => t . tld ) : null ;
}
// ---------- name pricing ----------
// Owner-set flat price first; else LENGTH-first tiers, then downshift for
// "less brandable" shapes (all-digits, hyphenated). Kept as multiplicative
// modifiers so tiers remain the primary story and mods just soften an
// edge case.
function priceForName ( label , tld ) {
2026-09-09 01:09:05 +02:00
const s = cleanLabel ( label ) ;
if ( ! s ) return { usd : 0 , tier : "invalid" } ;
feat(sirius-x): TLD owner panel — owner-set price, on/off switch, private TLDs
A TLD owner needed to run their namespace without the operator: set what
every name under the TLD sells for, take the TLD off the public registry
for a while, and still register names under it themselves.
Both settings live on chain in the TLD's TUPD records (`price`, `hidden`)
because changes are rare and every client already walks the TLD beacon.
The gateway's /api/tlds now carries records, owner, price_usd and
hidden_by per TLD; pricing.js quotes the owner price ahead of the length
tiers and only trusts a TLD_BEACON-sourced list; the register flow refuses
hidden and frozen TLDs for the public but lets the owner through at the
platform share only (the 90% owner cut would be paid to themselves). The
portal's TLDs tab loads real holdings, shows price and on/off state, and
gives each TLD a one-click switch, a price/policy editor and an inline
"register a name under .tld" form. Docs and the design table describe the
two new records.
2026-09-16 21:28:18 +02:00
const p = tld ? tldPolicy ( tld ) : null ;
if ( p && p . price != null ) {
return { usd : p . price , tier : "tld-set" , sats : usdToSats ( p . price ) , tld : p . tld , ownerSet : true } ;
}
2026-09-09 01:09:05 +02:00
let usd , tier ;
if ( s . length === 1 ) { usd = 5.00 ; tier = "premium-1" ; }
else if ( s . length === 2 ) { usd = 3.00 ; tier = "short-2" ; }
else if ( s . length === 3 ) { usd = 2.00 ; tier = "short-3" ; }
else if ( s . length <= 5 ) { usd = 1.00 ; tier = "standard" ; }
else if ( s . length <= 7 ) { usd = 0.50 ; tier = "long" ; }
else if ( s . length <= 16 ) { usd = 0.25 ; tier = "extended" ; }
else { usd = 0.10 ; tier = "very-long" ; }
// Modifiers.
if ( /^\d+$/ . test ( s ) ) { usd = round2 ( usd * 0.5 ) ; tier += "+digits" ; }
if ( s . includes ( "-" ) ) { usd = round2 ( usd * 0.7 ) ; tier += "+hyphen" ; }
return { usd , tier , sats : usdToSats ( usd ) } ;
}
feat(sirius-x): TLD owner panel — owner-set price, on/off switch, private TLDs
A TLD owner needed to run their namespace without the operator: set what
every name under the TLD sells for, take the TLD off the public registry
for a while, and still register names under it themselves.
Both settings live on chain in the TLD's TUPD records (`price`, `hidden`)
because changes are rare and every client already walks the TLD beacon.
The gateway's /api/tlds now carries records, owner, price_usd and
hidden_by per TLD; pricing.js quotes the owner price ahead of the length
tiers and only trusts a TLD_BEACON-sourced list; the register flow refuses
hidden and frozen TLDs for the public but lets the owner through at the
platform share only (the 90% owner cut would be paid to themselves). The
portal's TLDs tab loads real holdings, shows price and on/off state, and
gives each TLD a one-click switch, a price/policy editor and an inline
"register a name under .tld" form. Docs and the design table describe the
two new records.
2026-09-16 21:28:18 +02:00
// Per-label overrides — TLDs the operator wants priced outside the tiers.
// Cheap namespaces (`.test`, `.dev`, `.local`) exist for experimentation,
// school assignments and demo throwaways: length-tier pricing prices them
// like premium TLDs, which isn't the point. Add labels here to opt them
// out of the tier and (optionally) mark them renewal-free.
//
// Shape: label -> { usd, tier, noRenewal? }
const SPECIAL _TLDS = {
"test" : { usd : 0.01 , tier : "sandbox" , noRenewal : true } ,
} ;
2026-09-09 01:09:05 +02:00
function priceForTld ( label ) {
const s = cleanLabel ( label ) ;
if ( ! s ) return { usd : 0 , tier : "invalid" } ;
feat(sirius-x): TLD owner panel — owner-set price, on/off switch, private TLDs
A TLD owner needed to run their namespace without the operator: set what
every name under the TLD sells for, take the TLD off the public registry
for a while, and still register names under it themselves.
Both settings live on chain in the TLD's TUPD records (`price`, `hidden`)
because changes are rare and every client already walks the TLD beacon.
The gateway's /api/tlds now carries records, owner, price_usd and
hidden_by per TLD; pricing.js quotes the owner price ahead of the length
tiers and only trusts a TLD_BEACON-sourced list; the register flow refuses
hidden and frozen TLDs for the public but lets the owner through at the
platform share only (the 90% owner cut would be paid to themselves). The
portal's TLDs tab loads real holdings, shows price and on/off state, and
gives each TLD a one-click switch, a price/policy editor and an inline
"register a name under .tld" form. Docs and the design table describe the
two new records.
2026-09-16 21:28:18 +02:00
if ( Object . prototype . hasOwnProperty . call ( SPECIAL _TLDS , s ) ) {
const o = SPECIAL _TLDS [ s ] ;
return { usd : o . usd , tier : o . tier , sats : usdToSats ( o . usd ) , noRenewal : ! ! o . noRenewal } ;
}
2026-09-09 01:09:05 +02:00
let usd , tier ;
2026-09-09 03:45:41 +02:00
if ( s . length === 1 ) { usd = 500.00 ; tier = "premium-1" ; }
else if ( s . length === 2 ) { usd = 250.00 ; tier = "short-2" ; }
else if ( s . length === 3 ) { usd = 150.00 ; tier = "short-3" ; }
else if ( s . length <= 6 ) { usd = 100.00 ; tier = "standard" ; }
else if ( s . length <= 10 ) { usd = 50.00 ; tier = "long" ; }
else { usd = 25.00 ; tier = "extended" ; }
2026-09-09 01:09:05 +02:00
return { usd , tier , sats : usdToSats ( usd ) } ;
}
// Rounded USD → display string. Under $1 shows cents ($0.25); $1 and up
// shows whole dollars ($5) unless there's a fractional part.
function formatUsd ( usd ) {
const n = Number ( usd || 0 ) ;
if ( n >= 1 && n === Math . floor ( n ) ) return ` $ ${ n } ` ;
return ` $ ${ n . toFixed ( 2 ) } ` ;
}
feat(sirius-x): prices in BCH, amounts in bits, live BCH/USD rate from several exchanges
Every dollar figure on the site came from a hard-coded 250,000 sats per
dollar, which implies $400 per BCH; the market is near $250, so name
prices, TLD fees and sale listings were shown about 60% too cheap in
dollars. The gateway now serves /api/price: the median of Coinbase,
Kraken, Bitstamp, Binance and CoinGecko public tickers, no keys, cached
60 s, last good answer kept if every source fails. The site reads it
first, queries the same tickers itself if the gateway is unreachable,
remembers the last rate per browser, and only falls back to a constant
for the very first paint. Pages repaint when the rate arrives, and the
rate line says where it came from and how old it is.
Units: satoshi no longer appear anywhere. Sale prices, the seller's
input and the buy dialog are in BCH with the dollar figure beside
them; balances, fees and dust are in bits (1 bit = 100 satoshi).
2026-09-20 18:18:43 +02:00
// Money is shown in BCH (prices) and bits (fees, balances); 1 bit = 100
// satoshi, 1 BCH = 1,000,000 bits. Satoshi never appears in the UI.
const group = ( s ) => String ( s ) . replace ( /\B(?=(\d{3})+(?!\d))/g , "," ) ;
function formatBch ( sats ) {
const n = BigInt ( sats || 0 n ) ; const neg = n < 0 n ; const a = neg ? - n : n ;
const whole = a / 100000000 n , frac = ( a % 100000000 n ) . toString ( ) . padStart ( 8 , "0" ) . replace ( /0+$/ , "" ) ;
return ` ${ neg ? "-" : "" } ${ group ( whole ) } ${ frac ? "." + frac : "" } BCH ` ;
}
function formatBits ( sats ) {
const n = BigInt ( sats || 0 n ) ; const neg = n < 0 n ; const a = neg ? - n : n ;
const whole = a / 100 n , frac = ( a % 100 n ) . toString ( ) . padStart ( 2 , "0" ) . replace ( /0+$/ , "" ) ;
return ` ${ neg ? "-" : "" } ${ group ( whole ) } ${ frac ? "." + frac : "" } bits ` ;
2026-09-09 01:09:05 +02:00
}
feat(sirius-x): prices in BCH, amounts in bits, live BCH/USD rate from several exchanges
Every dollar figure on the site came from a hard-coded 250,000 sats per
dollar, which implies $400 per BCH; the market is near $250, so name
prices, TLD fees and sale listings were shown about 60% too cheap in
dollars. The gateway now serves /api/price: the median of Coinbase,
Kraken, Bitstamp, Binance and CoinGecko public tickers, no keys, cached
60 s, last good answer kept if every source fails. The site reads it
first, queries the same tickers itself if the gateway is unreachable,
remembers the last rate per browser, and only falls back to a constant
for the very first paint. Pages repaint when the rate arrives, and the
rate line says where it came from and how old it is.
Units: satoshi no longer appear anywhere. Sale prices, the seller's
input and the buy dialog are in BCH with the dollar figure beside
them; balances, fees and dust are in bits (1 bit = 100 satoshi).
2026-09-20 18:18:43 +02:00
const formatSats = formatBits ; // legacy name, same unit policy
const bchToSats = ( bch ) => BigInt ( Math . max ( 0 , Math . round ( Number ( String ( bch ) . replace ( /[^\d.]/g , "" ) ) * 1e8 ) ) ) ;
const satsToBch = ( sats ) => Number ( BigInt ( sats || 0 n ) ) / 1e8 ;
2026-09-09 01:09:05 +02:00
function round2 ( n ) { return Math . round ( Number ( n ) * 100 ) / 100 ; }
window . siriusPricing = {
priceForName ,
priceForTld ,
formatUsd ,
formatSats ,
feat(sirius-x): prices in BCH, amounts in bits, live BCH/USD rate from several exchanges
Every dollar figure on the site came from a hard-coded 250,000 sats per
dollar, which implies $400 per BCH; the market is near $250, so name
prices, TLD fees and sale listings were shown about 60% too cheap in
dollars. The gateway now serves /api/price: the median of Coinbase,
Kraken, Bitstamp, Binance and CoinGecko public tickers, no keys, cached
60 s, last good answer kept if every source fails. The site reads it
first, queries the same tickers itself if the gateway is unreachable,
remembers the last rate per browser, and only falls back to a constant
for the very first paint. Pages repaint when the rate arrives, and the
rate line says where it came from and how old it is.
Units: satoshi no longer appear anywhere. Sale prices, the seller's
input and the buy dialog are in BCH with the dollar figure beside
them; balances, fees and dust are in bits (1 bit = 100 satoshi).
2026-09-20 18:18:43 +02:00
formatBch ,
formatBits ,
bchToSats ,
satsToBch ,
2026-09-09 01:09:05 +02:00
usdToSats ,
feat(sirius-x): prices in BCH, amounts in bits, live BCH/USD rate from several exchanges
Every dollar figure on the site came from a hard-coded 250,000 sats per
dollar, which implies $400 per BCH; the market is near $250, so name
prices, TLD fees and sale listings were shown about 60% too cheap in
dollars. The gateway now serves /api/price: the median of Coinbase,
Kraken, Bitstamp, Binance and CoinGecko public tickers, no keys, cached
60 s, last good answer kept if every source fails. The site reads it
first, queries the same tickers itself if the gateway is unreachable,
remembers the last rate per browser, and only falls back to a constant
for the very first paint. Pages repaint when the rate arrives, and the
rate line says where it came from and how old it is.
Units: satoshi no longer appear anywhere. Sale prices, the seller's
input and the buy dialog are in BCH with the dollar figure beside
them; balances, fees and dust are in bits (1 bit = 100 satoshi).
2026-09-20 18:18:43 +02:00
// live rate (getters: callers that read the old constant see the current value)
get CHIPNET _SATS _PER _USD ( ) { return SATS _PER _USD ; } ,
get SATS _PER _USD ( ) { return SATS _PER _USD ; } ,
get bchUsd ( ) { return priceMeta . usd || 1e8 / SATS _PER _USD ; } ,
priceInfo ,
loadPrice ,
priceReady : ( ) => priceLoad || loadPrice ( ) ,
feat(sirius-x): TLD owner panel — owner-set price, on/off switch, private TLDs
A TLD owner needed to run their namespace without the operator: set what
every name under the TLD sells for, take the TLD off the public registry
for a while, and still register names under it themselves.
Both settings live on chain in the TLD's TUPD records (`price`, `hidden`)
because changes are rare and every client already walks the TLD beacon.
The gateway's /api/tlds now carries records, owner, price_usd and
hidden_by per TLD; pricing.js quotes the owner price ahead of the length
tiers and only trusts a TLD_BEACON-sourced list; the register flow refuses
hidden and frozen TLDs for the public but lets the owner through at the
platform share only (the 90% owner cut would be paid to themselves). The
portal's TLDs tab loads real holdings, shows price and on/off state, and
gives each TLD a one-click switch, a price/policy editor and an inline
"register a name under .tld" form. Docs and the design table describe the
two new records.
2026-09-16 21:28:18 +02:00
// TLD owner policy layer
loadTldPolicies ,
setTldPolicies ,
tldReady ,
tldPolicy ,
tldSellable ,
isTldOwner ,
visibleTlds ,
TLD _API ,
2026-09-09 01:09:05 +02:00
} ;
feat(sirius-x): TLD owner panel — owner-set price, on/off switch, private TLDs
A TLD owner needed to run their namespace without the operator: set what
every name under the TLD sells for, take the TLD off the public registry
for a while, and still register names under it themselves.
Both settings live on chain in the TLD's TUPD records (`price`, `hidden`)
because changes are rare and every client already walks the TLD beacon.
The gateway's /api/tlds now carries records, owner, price_usd and
hidden_by per TLD; pricing.js quotes the owner price ahead of the length
tiers and only trusts a TLD_BEACON-sourced list; the register flow refuses
hidden and frozen TLDs for the public but lets the owner through at the
platform share only (the 90% owner cut would be paid to themselves). The
portal's TLDs tab loads real holdings, shows price and on/off state, and
gives each TLD a one-click switch, a price/policy editor and an inline
"register a name under .tld" form. Docs and the design table describe the
two new records.
2026-09-16 21:28:18 +02:00
// Warm the policy cache as soon as the script lands; every consumer
// awaits tldReady() before it paints a price.
loadTldPolicies ( ) ;
2026-09-09 01:09:05 +02:00
} ) ( ) ;