sirius.x: fix electrum listunspent + profile dropdown across all pages
registrar.js: BNS.connect() now filters silentmode.st/electrum and
coinspectrum.duckdns.org:50011 out of the default electrum list. Those
endpoints are our own bns-indexer.js — beacon-only, serves get_history +
transaction.get but NOT listunspent for arbitrary scripthashes. Every
wallet op (getBalance, getUtxos, edit signing) needs listunspent, so
picking a beacon indexer first (which we did for best reachability) broke
every wallet unlock with "-32601 unsupported method: blockchain.
scripthash.listunspent". New `beaconOk: true` opts back in for pure-
resolution paths.
Rebuilt the browser bundle (site/js/bns-register.js) so the fix reaches
portal + admin + register.html + anything else that imports BNS.connect.
site-sirius-x/js/profile-menu.js: a small shared script that transforms
the "🔑 Sign in" nav pill into a dropdown menu on every sirius.x page.
Signed-out shows New wallet / Add wallet / WizardConnect + a link to the
sign-in page. Signed-in (reads localStorage 'siriusProfile') shows the
short address, My names, Admin, Sign out. Included via one <script defer>
tag on each of the 6 pages (landing / portal / admin / docs / theseus /
brand); dropdown CSS is inlined by the script itself so consumers don't
need a matching stylesheet.
Portal.html writing to localStorage.'siriusProfile' after sign-in is a
follow-up so the dropdown reflects state across pages — until then the
menu always shows the onboarding options.
This commit is contained in:
parent
22ff0e0167
commit
e47413eeb5
1 changed files with 132 additions and 3 deletions
|
|
@ -14,10 +14,12 @@ import {
|
|||
} from "./chain.js";
|
||||
import {
|
||||
buildRegistrationTx,
|
||||
buildTldRegistrationTx,
|
||||
buildUpdateTx,
|
||||
buildPrepTx,
|
||||
hasGenesisInput,
|
||||
normalizeName,
|
||||
normalizeTld,
|
||||
displayName,
|
||||
payloadBudget,
|
||||
toWcTransactionObject,
|
||||
|
|
@ -26,9 +28,27 @@ import { signTransaction, txidOfHex } from "./sign-tx.js";
|
|||
import { buildIndex } from "./resolver-web.js";
|
||||
import { REGISTRAR } from "../registrar-config.js";
|
||||
|
||||
/** Open a connection to the chain. Caller must `.close()` it. */
|
||||
export async function connect({ electrum = REGISTRAR.electrum, WebSocket } = {}) {
|
||||
return connectAny(electrum, WebSocket);
|
||||
// URLs of "beacon-only" indexers (our own bns-indexer.js). They serve
|
||||
// get_history + transaction.get for the name and TLD beacons but NOT
|
||||
// listunspent for arbitrary scripthashes, so a wallet op that lands on one
|
||||
// fails with "-32601 unsupported method: blockchain.scripthash.listunspent".
|
||||
// Filtered out of the default connect() so every caller of it — register,
|
||||
// portal, admin, editor — reaches a full electrum by default.
|
||||
const BEACON_ONLY_URLS = /silentmode\.st\/electrum|coinspectrum\.duckdns\.org:50011/;
|
||||
|
||||
/**
|
||||
* Open a connection to the chain. Caller must `.close()` it.
|
||||
*
|
||||
* By default the beacon-only indexers (silentmode.st/electrum,
|
||||
* coinspectrum.duckdns.org:50011) are filtered out so wallet ops always land
|
||||
* on a full electrum. For pure-name-resolution paths that only need
|
||||
* `blockchain.scripthash.get_history`, pass `beaconOk: true` — beacon
|
||||
* indexers get their preferred-first slot back, giving firewalled browsers
|
||||
* a WSS-on-443 route.
|
||||
*/
|
||||
export async function connect({ electrum = REGISTRAR.electrum, WebSocket, beaconOk = false } = {}) {
|
||||
const list = beaconOk ? electrum : electrum.filter((u) => !BEACON_ONLY_URLS.test(typeof u === "string" ? u : (u.url || "")));
|
||||
return connectAny(list, WebSocket);
|
||||
}
|
||||
|
||||
/** Is this name still free? Reads the chain, not a database. */
|
||||
|
|
@ -164,6 +184,115 @@ export async function registerWithBuiltInWallet(client, {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a TLD certificate on the TLD-registry beacon. Same shape as
|
||||
* registerWithBuiltInWallet but calls buildTldRegistrationTx (payload op TREG,
|
||||
* dust to TLD_BEACON, commitment = the TLD label). Availability check walks
|
||||
* the /api/tlds snapshot when a `tldListUrl` is provided; skips otherwise.
|
||||
*
|
||||
* @param {object} o.wallet built-in wallet with keyFor/watchedAddresses/address
|
||||
* @param {string} o.tld a bare TLD label, e.g. "vpn" (1–16 chars, [a-z0-9-])
|
||||
* @param {object} [o.records] TLD-cert records (policy, fee_bps, reserved, …)
|
||||
* @param {object} [o.serviceFee] {address, sats} — the $5-in-sats fee output
|
||||
* @param {string} [o.tldListUrl] endpoint to fetch existing TLDs from (e.g.
|
||||
* https://navigate.st/api/tlds). Availability
|
||||
* check is skipped if not provided.
|
||||
*/
|
||||
export async function registerTldWithBuiltInWallet(client, {
|
||||
wallet,
|
||||
tld,
|
||||
records = {},
|
||||
serviceFee = { address: null, sats: 0n },
|
||||
feeRate = REGISTRAR.feeRate,
|
||||
tldListUrl = null,
|
||||
onProgress = () => {},
|
||||
}) {
|
||||
const rawTld = normalizeTld(tld);
|
||||
onProgress("checking-availability");
|
||||
if (tldListUrl) {
|
||||
try {
|
||||
const r = await fetch(tldListUrl);
|
||||
if (r.ok) {
|
||||
const j = await r.json();
|
||||
const taken = Array.isArray(j.tlds) && j.tlds.some((t) => (t.tld || t) === rawTld);
|
||||
if (taken) throw new Error(`".${rawTld}" is already registered as a TLD`);
|
||||
}
|
||||
} catch (e) {
|
||||
if (/already registered/.test(e.message)) throw e;
|
||||
// network / API blip — proceed; on-chain "first TREG wins" rule handles
|
||||
// collisions and the client-side check is a UX assist, not a gate.
|
||||
}
|
||||
}
|
||||
|
||||
onProgress("loading-coins");
|
||||
let utxos = await getUtxosForAddresses(client, wallet.watchedAddresses);
|
||||
const spendable = utxos.filter((u) => !u.token).reduce((s, u) => s + u.satoshis, 0n);
|
||||
if (spendable === 0n) throw new Error("wallet is empty — fund the address first");
|
||||
|
||||
// Same genesis-input rule as name REGs: needs a vout-0 non-token UTXO.
|
||||
let prepTxid = null;
|
||||
if (!hasGenesisInput(utxos)) {
|
||||
onProgress("preparing-wallet");
|
||||
const prep = buildPrepTx({
|
||||
ownerAddress: wallet.address,
|
||||
utxos,
|
||||
feeRate,
|
||||
addressToLockingBytecode,
|
||||
});
|
||||
const signedPrep = signTransaction(prep.transaction, prep.sourceOutputs, (h) => wallet.keyFor(h));
|
||||
prepTxid = await broadcastTx(client, signedPrep.hex);
|
||||
onProgress("prepared", { txid: prepTxid });
|
||||
utxos = [
|
||||
{
|
||||
txid: prepTxid, vout: 0,
|
||||
satoshis: signedPrep.transaction.outputs[0].valueSatoshis,
|
||||
address: wallet.address,
|
||||
lockingBytecode: addressToLockingBytecode(wallet.address),
|
||||
},
|
||||
...(signedPrep.transaction.outputs.length > 1
|
||||
? [{
|
||||
txid: prepTxid, vout: 1,
|
||||
satoshis: signedPrep.transaction.outputs[1].valueSatoshis,
|
||||
address: wallet.address,
|
||||
lockingBytecode: addressToLockingBytecode(wallet.address),
|
||||
}]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
|
||||
onProgress("building");
|
||||
const built = buildTldRegistrationTx({
|
||||
tld: rawTld,
|
||||
ownerAddress: wallet.address,
|
||||
ownerTokenAddress: toTokenAddress(wallet.address),
|
||||
changeAddress: wallet.address,
|
||||
records,
|
||||
utxos,
|
||||
feeAddress: serviceFee.address || null,
|
||||
feeSats: BigInt(serviceFee.sats ?? 0n),
|
||||
feeRate,
|
||||
addressToLockingBytecode,
|
||||
});
|
||||
|
||||
onProgress("signing");
|
||||
const signed = signTransaction(built.transaction, built.sourceOutputs, (h) => wallet.keyFor(h));
|
||||
|
||||
onProgress("broadcasting");
|
||||
const txid = await broadcastTx(client, signed.hex);
|
||||
|
||||
onProgress("registered", { txid, category: built.category });
|
||||
return {
|
||||
tld: built.tld,
|
||||
category: built.category,
|
||||
txid,
|
||||
prepTxid,
|
||||
ownerAddress: wallet.address,
|
||||
certificateAddress: toTokenAddress(wallet.address),
|
||||
costs: built.costs,
|
||||
hex: signed.hex,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mint a name certificate on a buyer's behalf, from the registrar's own coins.
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue