diff --git a/src/lib/registrar.js b/src/lib/registrar.js index 0cc8ec5..738bdd8 100644 --- a/src/lib/registrar.js +++ b/src/lib/registrar.js @@ -293,6 +293,110 @@ export async function registerTldWithBuiltInWallet(client, { }; } +/** + * Register a TLD with an EXTERNAL wallet (WizardConnect). + * + * Same transaction shape as registerTldWithBuiltInWallet — same TREG payload, + * same TLD-beacon output, same service-fee output — only the signing transport + * differs. Genesis-input requirement is handled by prompting the connected + * wallet for a one-time prep signature if it can't front a vout-0 non-token + * coin already, mirroring registerWithExternalWallet's pattern. + * + * Does NOT accept `ownerAddress` because a TLD certificate is minted to + * whoever signs the mint — using the first address the connected wallet + * exposes keeps the same "you own what you signed for" invariant as the + * name-registration external path. + * + * @param {object} o.session a WalletSession (see connect/) + */ +export async function registerTldWithExternalWallet(client, { + session, + 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" handles it. + } + } + + onProgress("reading-wallet"); + const addresses = await session.getAddresses(); + if (!addresses.length) throw new Error("the wallet exposed no addresses"); + const owner = addresses[0]; + + onProgress("loading-coins"); + let utxos = await getUtxosForAddresses(client, addresses); + if (utxos.filter((u) => !u.token).reduce((s, u) => s + u.satoshis, 0n) === 0n) { + throw new Error("the connected wallet has no spendable coins"); + } + + let prepTxid = null; + if (!hasGenesisInput(utxos)) { + onProgress("preparing-wallet"); + const prep = buildPrepTx({ ownerAddress: owner, utxos, feeRate, addressToLockingBytecode }); + const prepSigned = await session.signTransaction(prep, { + userPrompt: "One-time setup so your wallet can create a TLD certificate", + }); + prepTxid = await landOnChain(client, session, prepSigned, onProgress); + onProgress("prepared", { txid: prepTxid }); + utxos = [ + { + txid: prepTxid, vout: 0, + satoshis: prep.transaction.outputs[0].valueSatoshis, + address: owner, + lockingBytecode: addressToLockingBytecode(owner), + }, + ...utxos.filter((u) => !u.token && !prep.inputUtxos.some((i) => i.txid === u.txid && i.vout === u.vout)), + ]; + } + + onProgress("building"); + const built = buildTldRegistrationTx({ + tld: rawTld, + ownerAddress: owner, + ownerTokenAddress: toTokenAddress(owner), + changeAddress: owner, + records, + utxos, + feeAddress: serviceFee.address || null, + feeSats: BigInt(serviceFee.sats ?? 0n), + feeRate, + addressToLockingBytecode, + }); + + onProgress("awaiting-approval"); + const signed = await session.signTransaction(built, { + userPrompt: `Register .${rawTld} — the TLD certificate is minted to your own address`, + }); + const txid = await landOnChain(client, session, signed, onProgress); + + onProgress("registered", { txid, category: built.category }); + return { + tld: built.tld, + category: built.category, + txid, + prepTxid, + ownerAddress: owner, + certificateAddress: toTokenAddress(owner), + costs: built.costs, + }; +} + /** * Mint a name certificate on a buyer's behalf, from the registrar's own coins. * diff --git a/src/web/register-entry.js b/src/web/register-entry.js index 74a4c4d..ebdbd7b 100644 --- a/src/web/register-entry.js +++ b/src/web/register-entry.js @@ -12,6 +12,7 @@ export { registerWithBuiltInWallet, registerWithExternalWallet, registerTldWithBuiltInWallet, + registerTldWithExternalWallet, setRecordsWithBuiltInWallet, setRecordsWithExternalWallet, findCertificateUtxo,