site/js/bns-register.js
Local Dev 326c2bd837 gateway: warn before serving a flagged name; expose what the lists say
A flagged name and its subdomains are served behind a warning that names the
reason, the list and the report transaction. The visitor may continue, per
name, for a day. The one refusal is the csam reason.

/api/blocklist returns the subscribed lists and current flags, and
/api/name/<name> carries the flag fields, for clients that cannot reach a
full electrum server. It is a convenience copy: every entry names its report
and can be re-derived from the list address.

Flags are read once a minute from full servers and shared across requests;
names are resolved without the extra round trip. BNS_BLOCKLISTS adds lists.

The mobile constants now carry the default subscriptions for the consumer to
come, and the docs explain flagged names.
2026-10-04 02:33:02 +02:00

7 lines
70 KiB
JavaScript

import{generateBip39Mnemonic as Jn,deriveSeedFromBip39Mnemonic as He,deriveHdPrivateNodeFromSeed as Qn,deriveHdPath as Zn,secp256k1 as Ke,hash160 as to,encodeCashAddress as vs,lockingBytecodeToCashAddress as eo,cashAddressToLockingBytecode as As,binToHex as no,hexToBin as Bs,utf8ToBin as je,binToBase64 as te,base64ToBin as ee}from"@bitauth/libauth";var Tt={chipnet:{id:"chipnet",label:"chipnet",prefix:"bchtest",beacon:"bchtest:qp8njaa9a3ahg7259s5qajnce526n0rttsq8qwx4jl",beaconScripthash:"e1cdbba719436f49acb4bf245b44df875c6d6b405eda46a9d3e736d138102f3b",tldBeacon:"bchtest:qzc4c76vtw9nd49g6x8nmvc0jz62uvk4dc7hmpmerh",tldBeaconScripthash:"c85db10e106606273422b2733b2a6fb8b35a4d0e7dbfb20cf59bdb0833c008b8",electrum:[{url:"wss://silentmode.st/electrum",ip:"195.184.247.106"},{url:"wss://coinspectrum.duckdns.org:50011",ip:"195.184.247.106"},{url:"wss://chipnet.bch.ninja:50004",ip:"135.148.236.244"},{url:"wss://chipnet.imaginary.cash:50004",ip:"66.29.152.199"},{url:"wss://tbch4.loping.net:62002",ip:"183.89.202.207"}],beaconOnly:/silentmode\.st\/electrum|coinspectrum\.duckdns\.org:50011/,electrumListName:"electrum.bch",rootTldName:"tlds.bch",bootstrapTlds:["bch"],startHeight:2e5,hostingBucket:"bns",nameListUrl:"https://dl.silentmode.st/bns-name-snapshot.json",nameListMirrorUrl:"https://navigate.st/bns/silentmode.bch/bns-name-snapshot.json",cdnIndexUrl:"https://dl.silentmode.st/bns-name-index.json",nostrTags:{names:"bns-name-list",tlds:"bns-tld-list"},explorerTx:"https://chipnet.imaginary.cash/tx/",faucets:[{name:"tbch.googol.cash",url:"https://tbch.googol.cash/"},{name:"chipnet.bch.ninja faucet",url:"https://chipnet.bch.ninja/faucet"}],operatorAddress:"bchtest:qzew3dqcsj0guwe5y3uxf9q2xs5ajgy9lca4z093cu",accountPath:"m/44'/145'/0'",bchnRpcPort:48332,blocklists:[{name:"sirius-blocklist.x",beacon:"bchtest:qrrnwvuasq5fy63mdvp3umx5qfavtu7mnq950rdssw",scripthash:"fa14c5fd7a22bd1856b44168823a1ab3ff50961c917dafbcba749aeaa7b26646"}]},mainnet:{id:"mainnet",label:"mainnet",prefix:"bitcoincash",beacon:null,beaconScripthash:null,tldBeacon:null,tldBeaconScripthash:null,electrum:[{url:"wss://bch.imaginary.cash:50004",ip:"67.223.119.97"},{url:"wss://electrum.imaginary.cash:50004",ip:"15.204.113.162"},{url:"wss://electroncash.dk:50004",ip:"87.63.200.138"},{url:"wss://bch.loping.net:50004",ip:"183.89.202.207"},{url:"wss://bch.soul-dev.com:50004",ip:"174.141.196.204"},{url:"wss://cashnode.bch.ninja:50004",ip:"135.148.236.246"}],beaconOnly:/(?!)/,electrumListName:"electrum.bch",rootTldName:"tlds.bch",bootstrapTlds:["bch"],startHeight:null,hostingBucket:"bns-mainnet",nameListUrl:"https://s3.silentmode.st:8600/bns-mainnet/name-list.json",cdnIndexUrl:"https://dl.silentmode.st/bns-mainnet-name-index.json",nostrTags:{names:"bns-mainnet-name-list",tlds:"bns-mainnet-tld-list"},explorerTx:"https://blockchair.com/bitcoin-cash/transaction/",faucets:[],operatorAddress:null,accountPath:"m/44'/145'/0'",bchnRpcPort:8332,blocklists:[]}},Re="chipnet";function Bn(){let t=typeof globalThis<"u"?globalThis.BNS_NETWORK:null,e=typeof process<"u"&&process.env?process.env.BNS_NETWORK:null;return String(t||e||Re).trim().toLowerCase()}function Et(t){if(t&&typeof t=="object")return t;let e=t||Bn(),n=Tt[e];if(!n)throw new Error(`unknown network "${e}" (known: ${Object.keys(Tt).join(", ")})`);return n}var E=Et();function qt(t=E){let e=Et(t);if(!e.beaconScripthash||!e.tldBeaconScripthash)throw new Error(`${e.label}: beacon addresses are not pinned yet (see ROADMAP-MAINNET.md \xA76)`);return e}var ys=E.beacon,ms=E.beaconScripthash,ws=E.tldBeacon,xs=E.tldBeaconScripthash,bs=E.electrum,ks=Tt.chipnet.electrum,Ss=E.electrumListName,Ae="BNS1";var Le="bch",In=/^[a-z0-9-]{1,32}$/,Ce=/^[a-z0-9-]{1,16}$/,De=/^[a-z0-9-]{1,32}(\.[a-z0-9-]{1,16})?$/;function Ue(t){return t.includes(".")?t:t+"."+Le}var Fe=t=>{let e=new Uint8Array(t.length/2);for(let n=0;n<e.length;n++)e[n]=parseInt(t.substr(n*2,2),16);return e},zt=t=>[...new TextEncoder().encode(t)].map(e=>e.toString(16).padStart(2,"0")).join("");function Pn(t){let n=String(t).toLowerCase().trim().replace(/^[a-z]+:\/\//,"").split("/")[0].replace(/\.$/,"").split(".").filter(Boolean);if(n.length===0)throw new Error(`invalid name "${t}"`);let o,s;if(n.length===1?(o=n[0],s=Le):(s=n[n.length-1],o=n[n.length-2]),!In.test(o)||!Ce.test(s))throw new Error(`invalid name "${t}" (allowed: <label>.<tld>, [a-z0-9-])`);return`${o}.${s}`}var Lt=class{constructor(e,n,o){this.url=e,this.WS=n||globalThis.WebSocket,this.options=o,this.id=0,this.pending=new Map,this.buf=""}connect(){return new Promise((e,n)=>{let o=this.options?new this.WS(this.url,this.options):new this.WS(this.url);this.ws=o;let s=async()=>{try{await this.call("server.version",["bns-web","1.4.2"]),e(this)}catch(i){n(i)}},r=i=>n(new Error("electrum ws error: "+(i?.message||this.url))),a=i=>this._onData(typeof i.data=="string"?i.data:i.data.toString());o.addEventListener?(o.addEventListener("open",s),o.addEventListener("error",r),o.addEventListener("message",a)):(o.onopen=s,o.onerror=r,o.onmessage=a)})}_handle(e){let n=this.pending.get(e.id);n&&(this.pending.delete(e.id),e.error?n.reject(new Error(JSON.stringify(e.error))):n.resolve(e.result))}_onData(e){this.buf+=e;let n;for(;(n=this.buf.indexOf(`
`))>=0;){let s=this.buf.slice(0,n).trim();if(this.buf=this.buf.slice(n+1),s)try{this._handle(JSON.parse(s))}catch{}}let o=this.buf.trim();if(o)try{let s=JSON.parse(o);this._handle(s),this.buf=""}catch{}}call(e,n=[]){let o=++this.id;return new Promise((s,r)=>{this.pending.set(o,{resolve:s,reject:r}),this.ws.send(JSON.stringify({id:o,method:e,params:n})+`
`),setTimeout(()=>{this.pending.has(o)&&(this.pending.delete(o),r(new Error(`electrum timeout: ${e}`)))},2e4)})}close(){try{this.ws.close()}catch{}}},Nn=t=>typeof t=="string"?{url:t}:t;async function Yt(t,e,{directIP:n=!1}={}){let o;for(let s of t){let r=Nn(s);try{return await new Lt(r.url,e).connect()}catch(a){o=a}if(n&&r.ip)try{let a=new URL(r.url),i=`${a.protocol}//${r.ip}:${a.port}`;return await new Lt(i,e,{servername:a.hostname}).connect()}catch(a){o=a}}throw o??new Error("no electrum server reachable")}function Vt(t){if(!t||!t.startsWith("6a"))return null;let e=Fe(t),n=1,o=[];for(;n<e.length;){let r=e[n++],a;if(r<=75)a=r;else if(r===76)a=e[n++];else if(r===77)a=e[n]|e[n+1]<<8,n+=2;else return null;o.push(e.slice(n,n+a)),n+=a}let s=new TextDecoder().decode(o[0]??new Uint8Array);if(!s.startsWith(Ae))return null;try{let r=JSON.parse(s.slice(Ae.length));if(!Rn.has(r.o))return null;if(typeof r.n=="string"||r.o==="RPT"&&Array.isArray(r.n)&&r.n.every(a=>typeof a=="string"))return r}catch{}return null}var Rn=new Set(["REG","UPD","REL","TREG","TUPD","RPT","BLK","UBK"]);function Xt(t,e){let n=t.height>0?t.height:1/0,o=e.height>0?e.height:1/0;return n-o||t.tx_hash.localeCompare(e.tx_hash)}function Jt(t,e){let n=new Set(t.map(i=>i.tx_hash)),o=new Map(t.map(i=>{let l=new Set;for(let c of e.get(i.tx_hash)?.vin??[])c.txid&&c.txid!==i.tx_hash&&n.has(c.txid)&&l.add(c.txid);return[i.tx_hash,l]})),s=new Set,r=[],a=[...t];for(;a.length;){let i=a.findIndex(c=>[...o.get(c.tx_hash)].every(f=>s.has(f)));i<0&&(i=0);let[l]=a.splice(i,1);s.add(l.tx_hash),r.push(l)}return r}function jt(t){let e=t.lastIndexOf(".");return e<0?t:t.slice(e+1)}function Ln(t,e,n){let o=n.get(t);return o?o.regHeight>0&&e>0?o.regHeight<=e:!0:!1}async function Qt(t,e=E){let{tldBeaconScripthash:n}=qt(e),o=typeof t.unionHistory=="function"?(await t.unionHistory(n)).history:await t.call("blockchain.scripthash.get_history",[n]);o.sort(Xt);let s=new Map;for(let{tx_hash:a}of o)try{s.set(a,await t.call("blockchain.transaction.get",[a,!0]))}catch{}let r=new Map;for(let{tx_hash:a,height:i}of Jt(o.filter(l=>s.has(l.tx_hash)),s)){let c=s.get(a).vout||[],f=null;for(let u of c)f=Vt(u.scriptPubKey?.hex)??f;if(!f||typeof f.n!="string"||!Ce.test(f.n))continue;let d=f.n;if(f.o==="TREG"){if(r.has(d))continue;let u=zt(d),h=c.find(m=>m.tokenData?.nft&&m.tokenData.nft.commitment===u);if(!h)continue;r.set(d,{category:h.tokenData.category,regHeight:i,regTxid:a,mintScriptHex:h.scriptPubKey?.hex??null,records:f.r??{},updates:[{height:i,txid:a,records:f.r??{}}]})}else if(f.o==="TUPD"){let u=r.get(d);if(!u||!c.some(h=>h.tokenData&&h.tokenData.category===u.category))continue;u.records=f.r??{},u.updates.push({height:i,txid:a,records:u.records})}}return r}function Cn(t,e){if(!t)return{};let n=t.updates||[];if(!n.length)return t.records||{};let o={};for(let s of n)e>0&&(s.height<=0||s.height>e)||(o=s.records||{});return o}function Dn(t,e){let n=Cn(t,e);return n.policy==="cosign"||n.hidden===1||n.hidden===!0||n.hidden==="1"}function Un(t,e,n){let o=zt(n);return(t||[]).some(s=>s.tokenData&&s.tokenData.category===e.category&&s.tokenData.nft&&s.tokenData.nft.commitment===o)}function Be(t){if(!t||!t.scriptPubKey)return null;let e=t.scriptPubKey;return typeof e.address=="string"?e.address:Array.isArray(e.addresses)&&e.addresses.length?e.addresses[0]:null}function $e(t,e){let n=t&&t.txid?e.get(t.txid):null;return!n||!Array.isArray(n.vout)?null:n.vout.find(o=>o&&o.n===t.vout)??n.vout[t.vout]??null}function Fn(t,e,n){return!t||!e||!e.category||(t.vout||[]).some(o=>o.tokenData&&o.tokenData.category===e.category)?!1:(t.vin||[]).some(o=>{let s=$e(o,n);return!!(s&&s.tokenData&&s.tokenData.nft&&s.tokenData.category===e.category)})}function $n(t,e){return t===void 0?!0:t>0?!(e>0)||e>t:!1}async function pt({network:t=E,electrum:e=Et(t).electrum,WebSocket:n,client:o,directIP:s=!1,enforceTld:r=!1,cosignGate:a=!0}={}){let i=qt(t),l=o??await Yt(e,n,{directIP:s});try{let c=null;if(r||a)try{c=await Qt(l,i)}catch(p){if(r)throw p;c=null}let f=null,d;if(typeof l.unionHistory=="function"){let p=await l.unionHistory(i.beaconScripthash);d=p.history,f={servers:p.servers,unreachable:p.unreachable,disagreements:p.disagreements}}else d=await l.call("blockchain.scripthash.get_history",[i.beaconScripthash]);d.sort(Xt);let u=new Map;for(let{tx_hash:p}of d)try{u.set(p,await l.call("blockchain.transaction.get",[p,!0]))}catch{}let h=new Map,m=new Map;for(let{tx_hash:p,height:y}of Jt(d.filter(g=>u.has(g.tx_hash)),u)){let g=u.get(p),w=g.vout||[],x=null;for(let b of w)x=Vt(b.scriptPubKey?.hex)??x;if(!x)continue;let v=x.n;if(!De.test(v))continue;let k=Ue(v);if(x.o==="REG"){if(h.has(k)||!$n(m.get(k),y)||r&&!Ln(jt(k),y,c))continue;if(a&&c){let T=c.get(jt(k));if(T&&Dn(T,y)&&!Un(w,T,jt(k)))continue}let b=zt(v),B=w.find(T=>T.tokenData?.nft&&T.tokenData.nft.commitment===b);if(!B)continue;h.set(k,{name:k,category:B.tokenData.category,records:x.r??{},txid:p,height:y,updatedTxid:null,owner:Be(B)}),m.delete(k)}else if(x.o==="REL"){let b=h.get(k);if(!b||!Fn(g,b,u))continue;h.delete(k),m.set(k,y)}else if(x.o==="UPD"){let b=h.get(k);if(!b)continue;let B=w.find(T=>T.tokenData&&T.tokenData.category===b.category);if(!B)continue;b.records=x.r??{},b.updatedTxid=p,b.owner=Be(B)??b.owner}}return f&&Object.defineProperty(h,"integrity",{value:f,enumerable:!1}),Object.defineProperty(h,"released",{value:m,enumerable:!1}),h}finally{o||l.close()}}var Mn=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),ot=(t,e)=>t>>>e|t<<32-e;function Hn(t){let e=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),n=t.length,o=new Uint8Array(n+9+63>>6<<6);o.set(t),o[n]=128;let s=new DataView(o.buffer);s.setUint32(o.length-8,Math.floor(n*8/4294967296)),s.setUint32(o.length-4,n*8>>>0);let r=new Uint32Array(64);for(let l=0;l<o.length;l+=64){for(let g=0;g<16;g++)r[g]=s.getUint32(l+g*4);for(let g=16;g<64;g++){let w=ot(r[g-15],7)^ot(r[g-15],18)^r[g-15]>>>3,x=ot(r[g-2],17)^ot(r[g-2],19)^r[g-2]>>>10;r[g]=r[g-16]+w+r[g-7]+x|0}let c=e[0],f=e[1],d=e[2],u=e[3],h=e[4],m=e[5],p=e[6],y=e[7];for(let g=0;g<64;g++){let w=y+(ot(h,6)^ot(h,11)^ot(h,25))+(h&m^~h&p)+Mn[g]+r[g]|0,x=(ot(c,2)^ot(c,13)^ot(c,22))+(c&f^c&d^f&d)|0;y=p,p=m,m=h,h=u+w|0,u=d,d=f,f=c,c=w+x|0}e[0]+=c,e[1]+=f,e[2]+=d,e[3]+=u,e[4]+=h,e[5]+=m,e[6]+=p,e[7]+=y}let a=new Uint8Array(32),i=new DataView(a.buffer);for(let l=0;l<8;l++)i.setUint32(l*4,e[l]);return a}var Me=t=>[...t].map(e=>e.toString(16).padStart(2,"0")).join(""),Kn="qpzry9x8gf2tvdw0s3jn54khce6mua7l";function Wn(t){let e=[0x98f2bc8e61n,0x79b76d99e2n,0xf33e5fb3c4n,0xae2eabe2a8n,0x1e4f43e470n],n=1n;for(let o of t){let s=n>>35n;n=(n&0x07ffffffffn)<<5n^BigInt(o);for(let r=0;r<5;r++)s>>BigInt(r)&1n&&(n^=e[r])}return n^1n}function Zt(t){let e=String(t).trim().toLowerCase(),n=e.indexOf(":");if(n<1)throw new Error(`address needs its network prefix: "${t}"`);let o=e.slice(0,n),s=[...e.slice(n+1)].map(u=>Kn.indexOf(u));if(s.length<9||s.some(u=>u<0))throw new Error(`not a cashaddr: "${t}"`);if(Wn([...[...o].map(u=>u.charCodeAt(0)&31),0,...s])!==0n)throw new Error(`bad cashaddr checksum: "${t}"`);let r=[],a=0,i=0;for(let u of s.slice(0,-8))for(a=a<<5|u,i+=5;i>=8;)i-=8,r.push(a>>i&255);let l=r[0],c=l>>3&15,f=Me(r.slice(1)),d=[20,24,28,32,40,48,56,64][l&7];if(f.length!==d*2)throw new Error(`cashaddr hash length mismatch: "${t}"`);if((c===0||c===2)&&d===20)return"76a914"+f+"88ac";if((c===1||c===3)&&d===20)return"a914"+f+"87";if((c===1||c===3)&&d===32)return"aa20"+f+"87";throw new Error(`unsupported address type: "${t}"`)}function Gn(t){return Me(Hn(Fe(Zt(t))).reverse())}var Ie=new Set(["phishing","impersonation","malware","scam-financial","csam","stolen"]);function Pe(t,e,n){return(t.vin||[]).some(o=>{let s=$e(o,n);return!!(s&&s.scriptPubKey&&s.scriptPubKey.hex===e)})}var jn=t=>typeof t=="string"&&/^[0-9a-f]{64}$/.test(t);function Ne(t){return typeof t=="string"&&De.test(t)?Ue(t):null}function qn({history:t,txs:e,address:n,source:o=null}){let s=Zt(n),r=e instanceof Map?e:new Map(Object.entries(e||{})),a=Jt([...t||[]].filter(f=>r.has(f.tx_hash)).sort(Xt),r),i=f=>{let d=null;for(let u of f.vout||[])d=Vt(u.scriptPubKey?.hex)??d;return d},l=new Map;for(let{tx_hash:f,height:d}of a){let u=r.get(f),h=i(u);if(!h||h.o!=="RPT"||!Pe(u,s,r)||h.t!=="block"&&h.t!=="unblock")continue;let m=(h.n||[]).map(Ne);!m.length||m.some(p=>!p)||h.t==="block"&&!Ie.has(h.r)||l.set(f,{type:h.t,names:new Set(m),reason:h.r??null,evidence:h.e??null,vote:h.v??null,height:d})}let c=new Map;for(let{tx_hash:f,height:d}of a){let u=r.get(f),h=i(u);if(!h||h.o!=="BLK"&&h.o!=="UBK"||!Pe(u,s,r))continue;let m=Ne(h.n);if(!m||!jn(h.p))continue;let p=l.get(h.p);if(!(!p||!p.names.has(m)))if(h.o==="BLK"){if(p.type!=="block"||!Ie.has(h.r)||p.reason!==h.r)continue;c.set(m,{name:m,reason:h.r,reportTxid:h.p,txid:f,height:d,evidence:h.e??p.evidence,vote:p.vote,source:o,address:n})}else{if(p.type!=="unblock")continue;c.delete(m)}}return c}function zn(t,e){let n=t&&t.name&&e&&typeof e.get=="function"?e.get(t.name)?.records?.bl:null;if(typeof n=="string")try{return Zt(n),n}catch{}return t?.beacon??null}async function Yn({network:t=E,lists:e,index:n=null,electrum:o,WebSocket:s,client:r,directIP:a=!1}={}){let i=Et(t),l=e??i.blocklists??[],c=new Map;if(!l.length)return c;let f=o??i.electrum.filter(u=>!i.beaconOnly.test(typeof u=="string"?u:u.url)),d=r??await Yt(f,s,{directIP:a});try{for(let u of l){let h=zn(u,n);if(!h)continue;let m=Gn(h),p=typeof d.unionHistory=="function"?(await d.unionHistory(m)).history:await d.call("blockchain.scripthash.get_history",[m]);if(!p||!p.length)continue;let y=new Map;for(let{tx_hash:g}of p)try{y.set(g,await d.call("blockchain.transaction.get",[g,!0]))}catch{}for(let[g,w]of qn({history:p,txs:y,address:h,source:u.name??h})){let x=c.get(g);x?x.sources.push(w):c.set(g,{...w,sources:[w]})}}return c}finally{r||d.close()}}function Vn(t,e){let n=t&&e&&typeof e.get=="function"?e.get(t.name):null;return n?{...t,blocked:!0,blockReason:n.reason,blockReportTxid:n.reportTxid,blockedBy:n.source}:t}async function Xn(t,e={}){let n=Pn(t),o=Et(e.network),s=e.blocklists===void 0?o.blocklists??[]:e.blocklists||[];if(!s.length)return(await pt(e)).get(n)??null;let r=e.client??await Yt(e.electrum??o.electrum,e.WebSocket,{directIP:!!e.directIP});try{let a=await pt({...e,client:r}),i=a.get(n)??null;if(!i)return null;let l=null;try{l=await Yn(e.client?{network:o,lists:s,index:a,client:e.client}:{network:o,lists:s,index:a,WebSocket:e.WebSocket,directIP:!!e.directIP})}catch{}return Vn(i,l)}finally{e.client||r.close()}}var Ts=E.rootTldName,Es=E.bootstrapTlds.slice();var oo="bchtest",so="bitcoincash",It=E.prefix,ne="m/44'/145'/0'",ro=5,Ct="bns.wallet.v1";function qe(t){let e=to(t);return Uint8Array.from([118,169,20,...e,136,172])}function We(t,e,n){let o=eo({bytecode:qe(t),prefix:e,tokenSupport:n});if(typeof o=="string")throw new Error(`address encoding failed: ${o}`);return o.address}var oe=class t{constructor(e,n,o=It,s=ne){this.mnemonic=e,this.prefix=o,this.accountPath=s,this._root=Qn(n),this._keys=new Map,this._derive()}static async create(e=It,n=ne){let o=Jn();return new t(o,await He(o),e,n)}static async fromMnemonic(e,n=It,o=ne){let s=String(e).trim().toLowerCase().replace(/\s+/g," "),r=await He(s);if(typeof r=="string")throw new Error(`invalid recovery phrase: ${r}`);return new t(s,r,n,o)}_derive(){this.receive=[],this.change=[];for(let[e,n]of[[0,this.receive],[1,this.change]])for(let o=0;o<ro;o++){let s=`${this.accountPath}/${e}/${o}`,r=Zn(this._root,s);if(typeof r=="string")throw new Error(`HD derivation failed at ${s}: ${r}`);let a=Ke.derivePublicKeyCompressed(r.privateKey);if(typeof a=="string")throw new Error(`pubkey derivation failed: ${a}`);let i=qe(a),l={path:s,branch:e,index:o,privateKey:r.privateKey,publicKey:a,lockingBytecode:i,address:We(a,this.prefix,!1),tokenAddress:We(a,this.prefix,!0)};n.push(l),this._keys.set(no(i),l)}}get address(){return this.receive[0].address}get tokenAddress(){return this.receive[0].tokenAddress}get watchedAddresses(){return[...this.receive,...this.change].map(e=>e.address)}keyFor(e){let n=this._keys.get(e);return n?{privateKey:n.privateKey,publicKey:n.publicKey}:null}signMessage(e,n){if(!(e instanceof Uint8Array)||e.length!==32)throw new Error("signMessage expects a 32-byte Uint8Array digest");let o=n||this.address,s=[...this.receive,...this.change].find(f=>f.address===o);if(!s)throw new Error(`no key for address ${o}`);let r=Ke.signMessageHashRecoverableCompact(s.privateKey,e);if(typeof r=="string")throw new Error(`signMessage failed: ${r}`);let{signature:a}=r,i=r.recoveryId??r.recovery;if(typeof i!="number")throw new Error("signMessage: no recovery id in signature result");let l=31+i,c=new Uint8Array(65);return c[0]=l,c.set(a,1),c}async encrypt(e){let n=crypto.getRandomValues(new Uint8Array(16)),o=crypto.getRandomValues(new Uint8Array(12)),s=await Ge(e,n),r=new Uint8Array(await crypto.subtle.encrypt({name:"AES-GCM",iv:o},s,je(this.mnemonic)));return JSON.stringify({v:1,kdf:"PBKDF2-SHA256",iterations:se,prefix:this.prefix,salt:te(n),iv:te(o),ct:te(r)})}static async decrypt(e,n){let o=typeof e=="string"?JSON.parse(e):e,s=await Ge(n,ee(o.salt),o.iterations??se),r;try{r=await crypto.subtle.decrypt({name:"AES-GCM",iv:ee(o.iv)},s,ee(o.ct))}catch{throw new Error("wrong password")}return t.fromMnemonic(new TextDecoder().decode(r),o.prefix??It)}async save(e,n=globalThis.localStorage){n.setItem(Ct,await this.encrypt(e))}static async load(e,n=globalThis.localStorage){let o=n.getItem(Ct);return o?t.decrypt(o,e):null}static exists(e=globalThis.localStorage){return!!e?.getItem(Ct)}static forget(e=globalThis.localStorage){e.removeItem(Ct)}},se=25e4;async function Ge(t,e,n=se){let o=await crypto.subtle.importKey("raw",je(String(t)),"PBKDF2",!1,["deriveKey"]);return crypto.subtle.deriveKey({name:"PBKDF2",salt:e,iterations:n,hash:"SHA-256"},o,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}var Rs=Tt.chipnet.electrum.map(t=>t.url),Dt=E.electrum.map(t=>t.url),re=class{constructor(e,n){this.url=e,this.WS=n||globalThis.WebSocket,this.id=0,this.pending=new Map,this.buf=""}connect(){return new Promise((e,n)=>{let o=new this.WS(this.url);this.ws=o;let s=async()=>{try{await this.call("server.version",["bns-web","1.4.2"]),e(this)}catch(i){n(i)}},r=i=>n(new Error("electrum ws error: "+(i?.message||this.url))),a=i=>this._onData(typeof i.data=="string"?i.data:i.data.toString());o.addEventListener?(o.addEventListener("open",s),o.addEventListener("error",r),o.addEventListener("message",a)):(o.onopen=s,o.onerror=r,o.onmessage=a)})}_handle(e){let n=this.pending.get(e.id);n&&(this.pending.delete(e.id),e.error?n.reject(new Error(JSON.stringify(e.error))):n.resolve(e.result))}_onData(e){this.buf+=e;let n;for(;(n=this.buf.indexOf(`
`))>=0;){let s=this.buf.slice(0,n).trim();if(this.buf=this.buf.slice(n+1),s)try{this._handle(JSON.parse(s))}catch{}}let o=this.buf.trim();if(o)try{let s=JSON.parse(o);this._handle(s),this.buf=""}catch{}}call(e,n=[]){let o=++this.id;return new Promise((s,r)=>{this.pending.set(o,{resolve:s,reject:r}),this.ws.send(JSON.stringify({id:o,method:e,params:n})+`
`),setTimeout(()=>{this.pending.has(o)&&(this.pending.delete(o),r(new Error(`electrum timeout: ${e}`)))},2e4)})}close(){try{this.ws.close()}catch{}}};async function ze(t=Dt,e){let n=[];for(let o of t)try{return await new re(o,e).connect()}catch(s){n.push(`${o}: ${s?.message??s}`)}throw io(t.length,n)}function io(t,e){let n=new Error(`no electrum server reachable (tried ${t}). These use non-standard ports, which some networks and VPNs block:
${e.join(`
`)}`);return n.code="NO_ELECTRUM",n.failures=e,n}import{cashAddressToLockingBytecode as ao,lockingBytecodeToCashAddress as co,sha256 as lo,hash160 as Ds,binToHex as uo,hexToBin as Us}from"@bitauth/libauth";function Ye(t){return uo(lo.hash(t).slice().reverse())}function C(t){let e=ao(t);if(typeof e=="string")throw new Error(`bad address "${t}": ${e}`);return e.bytecode}function ho(t){return Ye(C(t))}function j(t){let e=C(t),n=String(t).includes(":")?t.split(":")[0]:E.prefix,o=co({bytecode:e,prefix:n,tokenSupport:!0});if(typeof o=="string")throw new Error(`cannot token-encode "${t}": ${o}`);return o.address}async function Ve(t,e){let n=C(e),o=Ye(n);return(await t.call("blockchain.scripthash.listunspent",[o,"include_tokens"])).map(r=>({txid:r.tx_hash,vout:r.tx_pos,satoshis:BigInt(r.value),height:r.height,address:e,lockingBytecode:n,token:r.token_data?{category:r.token_data.category,amount:BigInt(r.token_data.amount??0),nft:r.token_data.nft?{capability:r.token_data.nft.capability,commitment:r.token_data.nft.commitment}:void 0}:void 0}))}async function q(t,e){let n=[];for(let o of e)n.push(...await Ve(t,o));return n}async function tt(t,e){return t.call("blockchain.transaction.broadcast",[e])}async function Xe(t,e,n=!0){return t.call("blockchain.transaction.get",[e,n])}import{binToHex as Pt,hexToBin as V,utf8ToBin as gt,binToUtf8 as ce,encodeTransaction as fo,decodeTransaction as Hs}from"@bitauth/libauth";var po="BNS1",ct=E.beacon,le=E.tldBeacon,A=600n,R=1000n,K=546n,ie="bch",ae=200,go=/^[a-z0-9-]{1,32}$/,Je=/^[a-z0-9-]{1,16}$/;function Ot(t){let e=String(t).toLowerCase().trim().replace(/^\.+/,"").replace(/\.+$/,"");if(!Je.test(e))throw new Error(`invalid TLD "${t}" (allowed: [a-z0-9-]{1,16})`);return e}var Ut=class extends Error{constructor(e){super(e),this.name="NoGenesisInputError",this.code="NO_GENESIS_INPUT"}},X=class extends Error{constructor(e,{needSats:n,haveSats:o}={}){super(e),this.name="InsufficientFundsError",this.code="INSUFFICIENT_FUNDS",this.needSats=n,this.haveSats=o}};function D(t){let e=String(t).toLowerCase().trim().replace(/\.$/,"").split(".").filter(Boolean),n,o;if(e.length===0)throw new Error(`invalid name "${t}"`);if(e.length===1?(n=e[0],o=ie):(o=e[e.length-1],n=e[e.length-2]),!go.test(n)||!Je.test(o))throw new Error(`invalid name "${t}" (allowed: <label>.<tld>, [a-z0-9-])`);return o===ie?n:`${n}.${o}`}function z(t){return t.includes(".")?t:`${t}.${ie}`}function J(t,e,n){let o=JSON.stringify({o:t,n:e,r:n??{}}),s=gt(po+o);if(s.length>ae)throw new Error(`records too large (${s.length} > ${ae} bytes)`);return s}function ue(t,e="REG"){return ae-J(e,t,{}).length}function yo(t){return t.length<=75?Uint8Array.from([t.length,...t]):t.length<=255?Uint8Array.from([76,t.length,...t]):Uint8Array.from([77,t.length&255,t.length>>8&255,...t])}function et(t){return Uint8Array.from([106,...yo(t)])}function de(){return Uint8Array.from([65,...new Uint8Array(65),33,...new Uint8Array(33)])}var mo=t=>!t.token;function F(t,e,{requireGenesis:n=!0}={}){let o=t.filter(mo),s=[],r=0n;if(n){let i=o.filter(l=>l.vout===0).sort((l,c)=>c.satoshis>l.satoshis?1:-1);if(i.length===0)throw new Ut("wallet has no coin at output index 0, which CashTokens requires to create a new certificate category \u2014 send a small amount to yourself first (buildPrepTx)");s.push(i[0]),r+=i[0].satoshis}let a=o.filter(i=>!s.some(l=>l.txid===i.txid&&l.vout===i.vout)).sort((i,l)=>l.satoshis>i.satoshis?1:-1);for(let i of a){if(r>=e)break;s.push(i),r+=i.satoshis}return{inputs:s,totalSats:r}}function yt(t){return{outpointTransactionHash:V(t.txid),outpointIndex:t.vout,sequenceNumber:4294967294,unlockingBytecode:de()}}function xt(t){return{...yt(t),lockingBytecode:t.lockingBytecode,valueSatoshis:t.satoshis,...t.token?{token:{amount:t.token.amount??0n,category:V(t.token.category),...t.token.nft?{nft:{capability:t.token.nft.capability,commitment:V(t.token.nft.commitment||"")}}:{}}}:{}}}function st(t){return fo(t).length}function he({name:t,ownerAddress:e,ownerTokenAddress:n,changeAddress:o,records:s={},utxos:r,feeAddress:a=null,feeSats:i=0n,tldFeeAddress:l=null,tldFeeSats:c=0n,beaconAddress:f=ct,feeRate:d=1,addressToLockingBytecode:u,cosignUtxo:h=null,cosignTokenAddress:m=null}){if(typeof u!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let p=D(t),y=J("REG",p,s),g=BigInt(i??0n),w=BigInt(c??0n);if(h&&!h.token?.nft)throw new Error("cosignUtxo does not carry a TLD certificate");let x=h?BigInt(h.satoshis):0n,v=h?u(m??n??e):null;if(g>0n&&!a)throw new Error("feeSats set but feeAddress missing");if(w>0n&&!l)throw new Error("tldFeeSats set but tldFeeAddress missing");let k=u(o??e),b=u(n??e),B=u(f),T=g>0n?u(a):null,S=w>0n?u(l):null,P=R+A+g+w+x,I=F(r,P+300n-x),O=0n,_=0n,U=!0;for(let N=0;N<6;N++){let{inputs:M}=I,H=h?[...M,h]:M,W=I.totalSats+x,ht=H[0],at=V(ht.txid),Y=(kt,St)=>{let Z=[];return kt&&Z.push({lockingBytecode:k,valueSatoshis:St}),Z.push({lockingBytecode:b,valueSatoshis:R,token:{amount:0n,category:at,nft:{capability:"none",commitment:gt(p)}}}),Z.push({lockingBytecode:et(y),valueSatoshis:0n}),Z.push({lockingBytecode:B,valueSatoshis:A}),S&&Z.push({lockingBytecode:S,valueSatoshis:w}),T&&Z.push({lockingBytecode:T,valueSatoshis:g}),h&&Z.push({lockingBytecode:v,valueSatoshis:x,token:{amount:BigInt(h.token.amount??0),category:V(h.token.category),nft:{capability:h.token.nft.capability,commitment:V(h.token.nft.commitment||"")}}}),{version:2,locktime:0,inputs:H.map(yt),outputs:Z}},_n=Y(!0,1000000n),vn=st(_n);O=BigInt(Math.ceil(vn*d));let Bt=P+O;if(W<Bt){let kt=F(r,Bt+200n-x);if(kt.inputs.length>M.length){I=kt;continue}throw new X(`not enough funds: need ${Bt} sat, have ${W} sat`,{needSats:Bt,haveSats:W})}if(_=W-Bt,U=_>=K,!U){let kt=Y(!1,0n);O=W-P;let St=BigInt(Math.ceil(st(kt)*d));if(O<St){let Z=F(r,P+St+K+200n-x);if(Z.inputs.length>M.length){I=Z;continue}throw new X(`not enough funds: need ${P+St} sat, have ${W} sat`,{needSats:P+St,haveSats:W})}_=0n}let An=Y(U,_),ft={},wt=0;return U&&(ft.change=wt++),ft.certificate=wt++,ft.opReturn=wt++,ft.beacon=wt++,S&&(ft.tldOwnerFee=wt++),T&&(ft.serviceFee=wt++),h&&(ft.cosignCertificate=wt++),{name:p,displayName:z(p),category:Pt(at),transaction:An,sourceOutputs:H.map(xt),inputUtxos:H,outputMap:ft,payload:y,costs:{certificateDustSats:R,beaconDustSats:A,serviceFeeSats:g,tldOwnerFeeSats:w,chainFeeSats:O,changeSats:_,totalInputSats:W,netCostSats:g+w+O+A}}}throw new Error("could not converge on a fee \u2014 please retry")}function Ft({tld:t,ownerAddress:e,ownerTokenAddress:n,changeAddress:o,records:s={},utxos:r,feeAddress:a=null,feeSats:i=0n,beaconAddress:l=le,feeRate:c=1,addressToLockingBytecode:f}){if(typeof f!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let d=Ot(t),u=J("TREG",d,s),h=BigInt(i??0n);if(h>0n&&!a)throw new Error("feeSats set but feeAddress missing");let m=f(o??e),p=f(n??e),y=f(l),g=h>0n?f(a):null,w=R+A+h,x=F(r,w+300n),v=0n,k=0n,b=!0;for(let B=0;B<6;B++){let{inputs:T,totalSats:S}=x,P=T[0],I=V(P.txid),O=(ht,at)=>{let Y=[];return ht&&Y.push({lockingBytecode:m,valueSatoshis:at}),Y.push({lockingBytecode:p,valueSatoshis:R,token:{amount:0n,category:I,nft:{capability:"none",commitment:gt(d)}}}),Y.push({lockingBytecode:et(u),valueSatoshis:0n}),Y.push({lockingBytecode:y,valueSatoshis:A}),g&&Y.push({lockingBytecode:g,valueSatoshis:h}),{version:2,locktime:0,inputs:T.map(yt),outputs:Y}},_=O(!0,1000000n),U=st(_);v=BigInt(Math.ceil(U*c));let N=w+v;if(S<N){let ht=F(r,N+200n);if(ht.inputs.length>T.length){x=ht;continue}throw new X(`not enough funds: need ${N} sat, have ${S} sat`,{needSats:N,haveSats:S})}if(k=S-N,b=k>=K,!b){let ht=O(!1,0n);v=S-w;let at=BigInt(Math.ceil(st(ht)*c));if(v<at){let Y=F(r,w+at+K+200n);if(Y.inputs.length>T.length){x=Y;continue}throw new X(`not enough funds: need ${w+at} sat, have ${S} sat`,{needSats:w+at,haveSats:S})}k=0n}let M=O(b,k),H={},W=0;return b&&(H.change=W++),H.certificate=W++,H.opReturn=W++,H.beacon=W++,g&&(H.serviceFee=W++),{tld:d,category:Pt(I),transaction:M,sourceOutputs:T.map(xt),inputUtxos:T,outputMap:H,payload:u,costs:{certificateDustSats:R,beaconDustSats:A,serviceFeeSats:h,chainFeeSats:v,changeSats:k,totalInputSats:S,netCostSats:h+v+A}}}throw new Error("could not converge on a fee \u2014 please retry")}function Nt({name:t,ownerAddress:e,ownerTokenAddress:n,records:o={},certificateUtxo:s,utxos:r,beaconAddress:a=ct,feeRate:i=1,addressToLockingBytecode:l}){if(typeof l!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let c=D(t);if(!s?.token?.nft)throw new Error("certificateUtxo does not carry a certificate");let f=Pt(gt(c));if(s.token.nft.commitment!==f)throw new Error(`certificate commitment mismatch: holds "${ce(V(s.token.nft.commitment))}", asked to update "${c}"`);let d=J("UPD",c,o),u=l(e),h=l(n??e),m=l(a),p=R+A,y=F(r,p+400n,{requireGenesis:!1}),g=0n,w=0n,x=!0;for(let v=0;v<6;v++){let k=[s,...y.inputs],b=s.satoshis+y.totalSats,B=(_,U)=>{let N=[];return _&&N.push({lockingBytecode:u,valueSatoshis:U}),N.push({lockingBytecode:h,valueSatoshis:R,token:{amount:0n,category:V(s.token.category),nft:{capability:"none",commitment:gt(c)}}}),N.push({lockingBytecode:et(d),valueSatoshis:0n}),N.push({lockingBytecode:m,valueSatoshis:A}),{version:2,locktime:0,inputs:k.map(yt),outputs:N}},T=st(B(!0,1000000n));g=BigInt(Math.ceil(T*i));let S=p+g;if(b<S){let _=F(r,S+200n,{requireGenesis:!1});if(_.inputs.length>y.inputs.length){y=_;continue}throw new X(`not enough funds: need ${S} sat, have ${b} sat`,{needSats:S,haveSats:b})}w=b-S,x=w>=K,x||(g=b-p,w=0n);let P=B(x,w),I={},O=0;return x&&(I.change=O++),I.certificate=O++,I.opReturn=O++,I.beacon=O++,{name:c,displayName:z(c),category:s.token.category,transaction:P,sourceOutputs:k.map(xt),inputUtxos:k,outputMap:I,payload:d,costs:{certificateDustSats:R,beaconDustSats:A,serviceFeeSats:0n,chainFeeSats:g,changeSats:w,totalInputSats:b,netCostSats:g+A}}}throw new Error("could not converge on a fee \u2014 please retry")}function fe({name:t,ownerAddress:e,certificateUtxo:n,utxos:o=[],beaconAddress:s=ct,feeRate:r=1,addressToLockingBytecode:a}){if(typeof a!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let i=D(t);if(!n?.token?.nft)throw new Error("certificateUtxo does not carry a certificate");let l=Pt(gt(i));if(n.token.nft.commitment!==l)throw new Error(`certificate commitment mismatch: holds "${ce(V(n.token.nft.commitment))}", asked to release "${i}"`);let c=J("REL",i,{}),f=a(e),d=a(s),u=A,h={inputs:[],totalSats:0n},m=0n,p=0n,y=!0;for(let g=0;g<6;g++){let w=[n,...h.inputs],x=n.satoshis+h.totalSats,v=(P,I)=>{let O=[];return P&&O.push({lockingBytecode:f,valueSatoshis:I}),O.push({lockingBytecode:et(c),valueSatoshis:0n}),O.push({lockingBytecode:d,valueSatoshis:A}),{version:2,locktime:0,inputs:w.map(yt),outputs:O}},k=BigInt(Math.ceil(st(v(!1,0n))*r)),b=BigInt(Math.ceil(st(v(!0,1000000n))*r));if(x<u+k){let P=F(o,u+b+200n,{requireGenesis:!1});if(P.inputs.length>h.inputs.length){h=P;continue}throw new X(`not enough funds: need ${u+k} sat, have ${x} sat`,{needSats:u+k,haveSats:x})}p=x-u-b,y=p>=K,y?m=b:(m=x-u,p=0n);let B=v(y,p),T={},S=0;return y&&(T.change=S++),T.opReturn=S++,T.beacon=S++,{name:i,displayName:z(i),category:n.token.category,transaction:B,sourceOutputs:w.map(xt),inputUtxos:w,outputMap:T,payload:c,costs:{beaconDustSats:A,chainFeeSats:m,changeSats:p,totalInputSats:x,netCostSats:m+A}}}throw new Error("could not converge on a fee \u2014 please retry")}function Qe({tld:t,ownerAddress:e,ownerTokenAddress:n,records:o={},certificateUtxo:s,utxos:r,beaconAddress:a=le,feeRate:i=1,addressToLockingBytecode:l}){if(typeof l!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let c=Ot(t);if(!s?.token?.nft)throw new Error("certificateUtxo does not carry a certificate");let f=Pt(gt(c));if(s.token.nft.commitment!==f)throw new Error(`TLD certificate commitment mismatch: holds "${ce(V(s.token.nft.commitment))}", asked to update ".${c}"`);let d=J("TUPD",c,o),u=l(e),h=l(n??e),m=l(a),p=R+A,y=F(r,p+400n,{requireGenesis:!1}),g=0n,w=0n,x=!0;for(let v=0;v<6;v++){let k=[s,...y.inputs],b=s.satoshis+y.totalSats,B=(_,U)=>{let N=[];return _&&N.push({lockingBytecode:u,valueSatoshis:U}),N.push({lockingBytecode:h,valueSatoshis:R,token:{amount:0n,category:V(s.token.category),nft:{capability:"none",commitment:gt(c)}}}),N.push({lockingBytecode:et(d),valueSatoshis:0n}),N.push({lockingBytecode:m,valueSatoshis:A}),{version:2,locktime:0,inputs:k.map(yt),outputs:N}},T=st(B(!0,1000000n));g=BigInt(Math.ceil(T*i));let S=p+g;if(b<S){let _=F(r,S+200n,{requireGenesis:!1});if(_.inputs.length>y.inputs.length){y=_;continue}throw new X(`not enough funds: need ${S} sat, have ${b} sat`,{needSats:S,haveSats:b})}w=b-S,x=w>=K,x||(g=b-p,w=0n);let P=B(x,w),I={},O=0;return x&&(I.change=O++),I.certificate=O++,I.opReturn=O++,I.beacon=O++,{tld:c,displayName:`.${c}`,category:s.token.category,transaction:P,sourceOutputs:k.map(xt),inputUtxos:k,outputMap:I,payload:d,costs:{certificateDustSats:R,beaconDustSats:A,serviceFeeSats:0n,chainFeeSats:g,changeSats:w,totalInputSats:b,netCostSats:g+A}}}throw new Error("could not converge on a fee \u2014 please retry")}function _t({ownerAddress:t,utxos:e,amountSats:n=10000n,feeRate:o=1,addressToLockingBytecode:s}){let r=s(t),a=F(e,n+400n,{requireGenesis:!1});if(a.inputs.length===0)throw new X("wallet is empty");for(let i=0;i<6;i++){let{inputs:l,totalSats:c}=a,f=(m,p)=>({version:2,locktime:0,inputs:l.map(yt),outputs:[{lockingBytecode:r,valueSatoshis:n},...m?[{lockingBytecode:r,valueSatoshis:p}]:[]]}),d=BigInt(Math.ceil(st(f(!0,1000000n))*o));if(c<n+d){let m=BigInt(Math.ceil(st(f(!1,0n))*o));if(c<=m+K)throw new X(`not enough funds to prepare the wallet: have ${c} sat`,{needSats:m+K,haveSats:c});return{transaction:{version:2,locktime:0,inputs:l.map(yt),outputs:[{lockingBytecode:r,valueSatoshis:c-m}]},sourceOutputs:l.map(xt),inputUtxos:l,costs:{chainFeeSats:m}}}let u=c-n-d,h=u>=K;return{transaction:f(h,u),sourceOutputs:l.map(xt),inputUtxos:l,costs:{chainFeeSats:h?d:c-n}}}throw new Error("could not converge on a fee \u2014 please retry")}function bt(t){return t.some(e=>e.vout===0&&!e.token)}function Ze({transaction:t,sourceOutputs:e},{broadcast:n=!0,userPrompt:o}={}){return{transaction:t,sourceOutputs:e,broadcast:n,...o?{userPrompt:o}:{}}}import{generateSigningSerializationBCH as wo,hash256 as en,secp256k1 as xo,encodeTransaction as bo,binToHex as pe}from"@bitauth/libauth";var tn=65;function rt(t,e,n){let o={...t,inputs:t.inputs.map((r,a)=>{let i=e[a],l=n(pe(i.lockingBytecode));if(!l)throw new Error(`no key for input ${a} \u2014 cannot sign`);let c=wo({inputIndex:a,sourceOutputs:e,transaction:t},{coveredBytecode:i.lockingBytecode,signingSerializationType:Uint8Array.of(tn)}),f=en(c),d=xo.signMessageHashSchnorr(l.privateKey,f);if(typeof d=="string")throw new Error(`signing failed on input ${a}: ${d}`);let u=Uint8Array.from([...d,tn]);return{...r,unlockingBytecode:Uint8Array.from([u.length,...u,l.publicKey.length,...l.publicKey])}})},s=bo(o);return{transaction:o,hex:pe(s),txid:ge(s)}}function ge(t){return pe(en(t).slice().reverse())}function ye(t){let e=new Uint8Array(t.length/2);for(let n=0;n<e.length;n++)e[n]=parseInt(t.substr(n*2,2),16);return ge(e)}var L={network:E.id,addressPrefix:E.prefix,explorerTx:E.explorerTx,selfHosted:null,get electrum(){return this.selfHosted?[this.selfHosted,...Dt]:Dt},beacon:E.beacon,tldBeacon:E.tldBeacon,serviceFee:{address:E.operatorAddress,sats:10000n},feeRate:1,wizardConnect:{dappName:"Silent Mode",dappIcon:"https://silentmode.st/icon.png"},faucets:E.faucets};function ko(t){let e=r=>(Number(r)/1e8).toFixed(8).replace(/0+$/,"").replace(/\.$/,""),n=[],o=BigInt(t.tldOwnerFeeSats??0n),s=BigInt(t.serviceFeeSats??0n);return o>0n?(n.push({label:"Service fee \u2014 TLD owner (90%)",sats:o,bch:e(o),note:"goes to whoever holds this TLD's certificate on chain"}),n.push({label:"Service fee \u2014 platform (10%)",sats:s,bch:e(s),note:"covers the resolver + gateway + indexer"})):n.push({label:"Service fee",sats:s,bch:e(s)}),n.push({label:"Network fee",sats:t.chainFeeSats,bch:e(t.chainFeeSats)}),n.push({label:"Discovery beacon",sats:t.beaconDustSats,bch:e(t.beaconDustSats)}),n.push({label:"Held in the certificate",sats:t.certificateDustSats,bch:e(t.certificateDustSats),note:"stays yours \u2014 it lives in the certificate you now own"}),n.push({label:"Total",sats:t.netCostSats,bch:e(t.netCostSats),total:!0}),n}import{hexToBin as ln,binToHex as mt,decodeTransaction as vo,lockingBytecodeToCashAddress as Ao,sha256 as Bo,utf8ToBin as Io,binToBase64 as Po}from"@bitauth/libauth";import{encodeTransaction as we,decodeTransaction as nn,generateSigningSerializationBCH as on,hash256 as xe,hash160 as So,secp256k1 as sn,hexToBin as lt,binToHex as $,utf8ToBin as rn}from"@bitauth/libauth";var $t=65,me=195,it=(t,e)=>({outpointTransactionHash:lt(t.txid),outpointIndex:t.vout,sequenceNumber:4294967294,unlockingBytecode:e??de()}),ut=t=>t?{amount:BigInt(t.amount??0),category:lt(t.category),...t.nft?{nft:{capability:t.nft.capability,commitment:lt(t.nft.commitment||"")}}:{}}:void 0,be=t=>({lockingBytecode:t.lockingBytecode instanceof Uint8Array?$(t.lockingBytecode):String(t.lockingBytecode),valueSatoshis:String(t.valueSatoshis),...t.token?{token:{category:t.token.category instanceof Uint8Array?$(t.token.category):String(t.token.category),amount:String(t.token.amount??0),...t.token.nft?{nft:{capability:t.token.nft.capability,commitment:t.token.nft.commitment instanceof Uint8Array?$(t.token.nft.commitment):String(t.token.nft.commitment||"")}}:{}}}:{}}),ke=t=>({lockingBytecode:lt(t.lockingBytecode),valueSatoshis:BigInt(t.valueSatoshis??t.satoshis),...t.token?{token:ut(t.token)}:{}}),dt=(t,e)=>({...it(t,e),lockingBytecode:t.lockingBytecode instanceof Uint8Array?t.lockingBytecode:lt(t.lockingBytecode),valueSatoshis:BigInt(t.satoshis),...t.token?{token:ut(t.token)}:{}});function an(t,e,n,o,s){let r=e[n],a=on({inputIndex:n,sourceOutputs:e,transaction:t},{coveredBytecode:r.lockingBytecode,signingSerializationType:Uint8Array.of(s)}),i=xe(a),l=sn.signMessageHashSchnorr(o.privateKey,i);if(typeof l=="string")throw new Error(`signing failed on input ${n}: ${l}`);let c=Uint8Array.from([...l,s]);return Uint8Array.from([c.length,...c,o.publicKey.length,...o.publicKey])}function vt(t,e,n,{sighash:o=$t,skip:s=[]}={}){let r={...t,inputs:t.inputs.map((i,l)=>{if(s.includes(l))return i;let c=n($(e[l].lockingBytecode));return c?{...i,unlockingBytecode:an(t,e,l,c,o)}:i})},a=we(r);return{transaction:r,hex:$(a),txid:$(xe(a).slice().reverse())}}function To({name:t,certificateUtxo:e,priceSats:n,sellerAddress:o,addressToLockingBytecode:s,keyFor:r}){let a=D(t),i=BigInt(n);if(i<K)throw new Error(`price must be at least ${K} sat`);if(!e?.token?.nft)throw new Error("certificateUtxo does not carry a certificate");if(e.token.nft.commitment!==$(rn(a)))throw new Error("certificate commitment does not match the name");let l=s(o),c=dt(e),f={version:2,locktime:0,inputs:[it(e)],outputs:[{lockingBytecode:l,valueSatoshis:i}]},d=r($(c.lockingBytecode));if(!d)throw new Error("this wallet does not hold the key for the certificate");return f.inputs[0].unlockingBytecode=an(f,[c],0,d,me),{v:1,name:a,seller:o,price_sats:i.toString(),category:e.token.category,partial_tx:$(we(f)),source_output:{txid:e.txid,vout:e.vout,satoshis:BigInt(e.satoshis).toString(),lockingBytecode:$(c.lockingBytecode),token:{category:e.token.category,amount:String(e.token.amount??0),nft:e.token.nft?{capability:e.token.nft.capability,commitment:e.token.nft.commitment}:void 0}},created_at:new Date().toISOString()}}function Eo(t){let e=t[0],n=t.slice(1,1+e),o=t[1+e],s=t.slice(2+e,2+e+o);return{sig:n,pubkey:s}}var Oo=t=>Uint8Array.from([118,169,20,...So(t),136,172]);function cn(t,e={}){let n=o=>({ok:!1,error:o});try{if(!t||t.v!==1)return n("unsupported listing version");let o=D(t.name),s=nn(lt(t.partial_tx));if(typeof s=="string")return n("partial_tx does not decode: "+s);if(s.inputs.length!==1||s.outputs.length<1)return n("partial_tx must have exactly one input and a payment output");let r=t.source_output;if(!r?.token?.nft)return n("source_output carries no certificate");if($(s.inputs[0].outpointTransactionHash)!==r.txid||s.inputs[0].outpointIndex!==r.vout)return n("input does not spend source_output");if(r.token.nft.commitment!==$(rn(o)))return n("certificate commitment does not match the name");if(e.category&&r.token.category!==e.category)return n("certificate category does not match the registry");let a=s.outputs[0].valueSatoshis;if(a<K)return n("price below dust");if(BigInt(t.price_sats)!==a)return n("price_sats does not match output 0");let{sig:i,pubkey:l}=Eo(s.inputs[0].unlockingBytecode);if(i.length!==65||i[64]!==me)return n("input 0 is not signed SINGLE|ANYONECANPAY");let c=lt(r.lockingBytecode);if($(Oo(l))!==$(c))return n("public key does not own the certificate output");if(e.ownerLockingBytecode&&$(c)!==e.ownerLockingBytecode)return n("certificate is not held by the current owner");if($(s.outputs[0].lockingBytecode)!==$(c))return n("payment does not go to the certificate holder");let f={...s.inputs[0],lockingBytecode:c,valueSatoshis:BigInt(r.satoshis),token:ut(r.token)},d=on({inputIndex:0,sourceOutputs:[f],transaction:s},{coveredBytecode:c,signingSerializationType:Uint8Array.of(me)});return sn.verifySignatureSchnorr(i.slice(0,64),l,xe(d))?{ok:!0,name:o,price_sats:a,seller_lock:$(c),category:r.token.category}:n("seller signature is invalid")}catch(o){return n(String(o?.message||o))}}function _o({listing:t,buyerAddress:e,buyerTokenAddress:n,utxos:o,records:s={},beaconAddress:r=ct,feeRate:a=1,addressToLockingBytecode:i}){let l=cn(t);if(!l.ok)throw new Error("bad listing: "+l.error);let c=l.name,f=nn(lt(t.partial_tx)),d=t.source_output,u=f.inputs[0],h={...u,lockingBytecode:lt(d.lockingBytecode),valueSatoshis:BigInt(d.satoshis),token:ut(d.token)},m=f.outputs[0],p=m.valueSatoshis,y=i(e),g=i(n??e),w=i(r),x=J("UPD",c,s),v=p+R+A-BigInt(d.satoshis),k=F(o,v+400n,{requireGenesis:!1}),b=B=>we(B).length;for(let B=0;B<6;B++){let{inputs:T,totalSats:S}=k,P=(M,H)=>({version:2,locktime:0,inputs:[u,...T.map(W=>it(W))],outputs:[m,{lockingBytecode:g,valueSatoshis:R,token:ut(d.token)},{lockingBytecode:et(x),valueSatoshis:0n},{lockingBytecode:w,valueSatoshis:A},...M?[{lockingBytecode:y,valueSatoshis:H}]:[]]}),I=BigInt(Math.ceil(b(P(!0,1000000n))*a)),O=v+I;if(S<O){let M=F(o,O+200n,{requireGenesis:!1});if(M.inputs.length>T.length){k=M;continue}let H=new Error(`not enough funds: need ${O} sat, have ${S} sat`);throw H.needSats=O,H.haveSats=S,H}let _=S-O,U=_>=K;U||(_=0n);let N=P(U,_);return{name:c,transaction:N,sourceOutputs:[h,...T.map(M=>dt(M))],sellerInputIndex:0,costs:{priceSats:p,beaconDustSats:A,certificateDustSats:R,chainFeeSats:U?I:S-v,changeSats:_,totalInputSats:S}}}throw new Error("could not converge on a fee \u2014 please retry")}var No=90n,Ro=100n;async function un(t,e,n=L.addressPrefix){let s=(await Qt(t)).get(String(e).toLowerCase());if(!s?.mintScriptHex)return null;try{let r=ln(s.mintScriptHex),a=Ao({bytecode:r,prefix:n,tokenSupport:!1});return typeof a=="string"?null:a.address}catch{return null}}function dn(t){let e=BigInt(t??0n),n=e*No/Ro,o=e-n;return{tldOwnerFeeSats:n,platformFeeSats:o}}var Lo=E.beaconOnly;async function Co({electrum:t=L.electrum,WebSocket:e,beaconOk:n=!1}={}){let o=n?t:t.filter(s=>!Lo.test(typeof s=="string"?s:s.url||""));return ze(o,e)}async function Mt(t,e){let n=D(e),o=z(n),s=await pt({client:t}),r=s.get(o)??null,a=s.released?.get(o),i=a!==void 0&&!(a>0);return{name:o,rawName:n,available:!r&&!i,entry:r,releasedAt:a??null,releasePending:i,reason:r?"registered":i?"release-pending":null,recordBudgetBytes:ue(n)}}async function Ht(t,{name:e,ownerAddress:n,records:o={},utxos:s,serviceFee:r=L.serviceFee,feeRate:a=L.feeRate,cosignUtxo:i=null,cosignTokenAddress:l=null}){let c=s??await q(t,[n]),f=D(e),d=f.indexOf("."),u=d<0?"bch":f.slice(d+1),h=BigInt(r.sats??0n),m=null,p=0n,y=h;if(h>0n){try{m=await un(t,u)}catch{m=null}if(m&&m!==n){let g=dn(h);p=g.tldOwnerFeeSats,y=g.platformFeeSats}}return he({name:e,ownerAddress:n,ownerTokenAddress:j(n),records:o,utxos:c,feeAddress:y>0n?r.address:null,feeSats:y,tldFeeAddress:p>0n?m:null,tldFeeSats:p,beaconAddress:L.beacon,feeRate:a,addressToLockingBytecode:C,cosignUtxo:i,cosignTokenAddress:l})}function hn(t,{tld:e,category:n}){let o=[...new TextEncoder().encode(String(e).toLowerCase())].map(s=>s.toString(16).padStart(2,"0")).join("");return t.find(s=>s.token?.nft&&(!n||s.token.category===n)&&s.token.nft.commitment===o)??null}async function fn(t,{wallet:e,utxos:n,feeRate:o,onProgress:s}){if(bt(n))return{utxos:n,prepTxid:null};s("preparing-wallet");let r=_t({ownerAddress:e.address,utxos:n,feeRate:o,addressToLockingBytecode:C}),a=rt(r.transaction,r.sourceOutputs,c=>e.keyFor(c)),i=await tt(t,a.hex);return s("prepared",{txid:i}),{utxos:a.transaction.outputs.slice(0,2).map((c,f)=>({txid:i,vout:f,satoshis:c.valueSatoshis,address:e.address,lockingBytecode:C(e.address)})),prepTxid:i}}async function Do(t,{wallet:e,name:n,records:o={},serviceFee:s=L.serviceFee,feeRate:r=L.feeRate,tldCert:a,onProgress:i=()=>{}}){if(!a?.utxo?.token?.nft||!a.owner)throw new Error("tldCert must be the /api/tld-cert response ({ owner, utxo })");i("checking-availability");let l=await Mt(t,n);if(!l.available)throw new Error(l.releasePending?`"${l.name}" was just released \u2014 it can be registered once that release is in a block`:`"${l.name}" is already registered`);i("loading-coins");let c=await q(t,e.watchedAddresses);if(c.filter(y=>!y.token).reduce((y,g)=>y+g.satoshis,0n)===0n)throw new Error("wallet is empty \u2014 fund the address first");let d=null;({utxos:c,prepTxid:d}=await fn(t,{wallet:e,utxos:c,feeRate:r,onProgress:i})),i("building");let u=await Ht(t,{name:n,ownerAddress:e.address,records:o,utxos:c,serviceFee:s,feeRate:r,cosignUtxo:{...a.utxo,address:a.owner},cosignTokenAddress:j(a.owner)}),h=u.transaction.inputs.length-1;i("signing");let m={...u.transaction,inputs:u.transaction.inputs.map((y,g)=>g===h?{...y,unlockingBytecode:new Uint8Array}:y)},p=vt(m,u.sourceOutputs,y=>e.keyFor(y),{skip:[h]});return{name:u.name,displayName:u.displayName,category:u.category,tx:p.hex,sourceOutputs:u.sourceOutputs.map(be),cosignInputIndex:h,costs:u.costs,prepTxid:d}}function Uo(t,e){let n=vo(ln(String(t.tx||"")));if(typeof n=="string")throw new Error("invalid transaction: "+n);let o=(t.sourceOutputs||[]).map(ke);if(o.length!==n.inputs.length)throw new Error("sourceOutputs do not match the transaction inputs");let s=[],r=0;n.inputs.forEach((i,l)=>{let c=o[l];if(!e(mt(c.lockingBytecode))){s.push(l);return}if(c.token?.nft&&!n.outputs.some(d=>d.token&&mt(d.token.category)===mt(c.token.category)&&mt(d.token.nft?.commitment??new Uint8Array)===mt(c.token.nft.commitment??new Uint8Array)&&mt(d.lockingBytecode)===mt(c.lockingBytecode)&&d.valueSatoshis>=c.valueSatoshis))throw new Error("refusing to sign: the request would not return your TLD certificate to you");r+=1});let a=vt(n,o,e,{skip:s});return{hex:a.hex,txid:a.txid,signedInputs:r}}async function Fo(t,{wallet:e,name:n,records:o={},serviceFee:s=L.serviceFee,feeRate:r=L.feeRate,cosign:a=null,onProgress:i=()=>{}}){i("checking-availability");let l=await Mt(t,n);if(!l.available)throw new Error(l.releasePending?`"${l.name}" was just released \u2014 it can be registered once that release is in a block`:`"${l.name}" is already registered`);i("loading-coins");let c=await q(t,e.watchedAddresses);if(c.filter(y=>!y.token).reduce((y,g)=>y+g.satoshis,0n)===0n)throw new Error("wallet is empty \u2014 fund the address first");let d=null;if(a&&(d=hn(c,a),!d))throw new Error(`this wallet does not hold the certificate for .${a.tld} \u2014 only the TLD owner can co-sign`);let u=null;({utxos:c,prepTxid:u}=await fn(t,{wallet:e,utxos:c,feeRate:r,onProgress:i})),i("building");let h=await Ht(t,{name:n,ownerAddress:e.address,records:o,utxos:c,serviceFee:s,feeRate:r,cosignUtxo:d,cosignTokenAddress:d?j(e.address):null});i("signing");let m=rt(h.transaction,h.sourceOutputs,y=>e.keyFor(y));i("broadcasting");let p=await tt(t,m.hex);return i("registered",{txid:p,category:h.category}),{name:h.name,displayName:h.displayName,category:h.category,txid:p,prepTxid:u,ownerAddress:e.address,certificateAddress:j(e.address),costs:h.costs,hex:m.hex}}async function $o(t,{wallet:e,tld:n,records:o={},serviceFee:s={address:null,sats:0n},feeRate:r=L.feeRate,tldListUrl:a=null,onProgress:i=()=>{}}){let l=Ot(n);if(i("checking-availability"),a)try{let p=await fetch(a);if(p.ok){let y=await p.json();if(Array.isArray(y.tlds)&&y.tlds.some(w=>(w.tld||w)===l))throw new Error(`".${l}" is already registered as a TLD`)}}catch(p){if(/already registered/.test(p.message))throw p}i("loading-coins");let c=await q(t,e.watchedAddresses);if(c.filter(p=>!p.token).reduce((p,y)=>p+y.satoshis,0n)===0n)throw new Error("wallet is empty \u2014 fund the address first");let d=null;if(!bt(c)){i("preparing-wallet");let p=_t({ownerAddress:e.address,utxos:c,feeRate:r,addressToLockingBytecode:C}),y=rt(p.transaction,p.sourceOutputs,g=>e.keyFor(g));d=await tt(t,y.hex),i("prepared",{txid:d}),c=[{txid:d,vout:0,satoshis:y.transaction.outputs[0].valueSatoshis,address:e.address,lockingBytecode:C(e.address)},...y.transaction.outputs.length>1?[{txid:d,vout:1,satoshis:y.transaction.outputs[1].valueSatoshis,address:e.address,lockingBytecode:C(e.address)}]:[]]}i("building");let u=Ft({tld:l,ownerAddress:e.address,ownerTokenAddress:j(e.address),changeAddress:e.address,records:o,utxos:c,feeAddress:s.address||null,feeSats:BigInt(s.sats??0n),feeRate:r,addressToLockingBytecode:C});i("signing");let h=rt(u.transaction,u.sourceOutputs,p=>e.keyFor(p));i("broadcasting");let m=await tt(t,h.hex);return i("registered",{txid:m,category:u.category}),{tld:u.tld,category:u.category,txid:m,prepTxid:d,ownerAddress:e.address,certificateAddress:j(e.address),costs:u.costs,hex:h.hex}}async function Mo(t,{session:e,tld:n,records:o={},serviceFee:s={address:null,sats:0n},feeRate:r=L.feeRate,tldListUrl:a=null,onProgress:i=()=>{}}){let l=Ot(n);if(i("checking-availability"),a)try{let y=await fetch(a);if(y.ok){let g=await y.json();if(Array.isArray(g.tlds)&&g.tlds.some(x=>(x.tld||x)===l))throw new Error(`".${l}" is already registered as a TLD`)}}catch(y){if(/already registered/.test(y.message))throw y}i("reading-wallet");let c=await e.getAddresses();if(!c.length)throw new Error("the wallet exposed no addresses");let f=c[0];i("loading-coins");let d=await q(t,c);if(d.filter(y=>!y.token).reduce((y,g)=>y+g.satoshis,0n)===0n)throw new Error("the connected wallet has no spendable coins");let u=null;if(!bt(d)){i("preparing-wallet");let y=_t({ownerAddress:f,utxos:d,feeRate:r,addressToLockingBytecode:C}),g=await e.signTransaction(y,{userPrompt:"One-time setup so your wallet can create a TLD certificate"});u=await Rt(t,e,g,i),i("prepared",{txid:u}),d=[{txid:u,vout:0,satoshis:y.transaction.outputs[0].valueSatoshis,address:f,lockingBytecode:C(f)},...d.filter(w=>!w.token&&!y.inputUtxos.some(x=>x.txid===w.txid&&x.vout===w.vout))]}i("building");let h=Ft({tld:l,ownerAddress:f,ownerTokenAddress:j(f),changeAddress:f,records:o,utxos:d,feeAddress:s.address||null,feeSats:BigInt(s.sats??0n),feeRate:r,addressToLockingBytecode:C});i("awaiting-approval");let m=await e.signTransaction(h,{userPrompt:`Register .${l} \u2014 the TLD certificate is minted to your own address`}),p=await Rt(t,e,m,i);return i("registered",{txid:p,category:h.category}),{tld:h.tld,category:h.category,txid:p,prepTxid:u,ownerAddress:f,certificateAddress:j(f),costs:h.costs}}async function Rt(t,e,n,o){let s=n.signedTransactionHash??ye(n.signedTransaction);if(e.walletBroadcasts)return s;o("broadcasting");try{return await tt(t,n.signedTransaction)}catch(r){let a=String(r?.message??r);if(/already|duplicate|txn-already|in mempool/i.test(a))return s;throw r}}async function Ho(t,{session:e,name:n,ownerAddress:o,records:s={},serviceFee:r=L.serviceFee,feeRate:a=L.feeRate,onProgress:i=()=>{}}){i("checking-availability");let l=await Mt(t,n);if(!l.available)throw new Error(l.releasePending?`"${l.name}" was just released \u2014 it can be registered once that release is in a block`:`"${l.name}" is already registered`);i("reading-wallet");let c=await e.getAddresses();if(!c.length)throw new Error("the wallet exposed no addresses");let f=o??c[0];i("loading-coins");let d=await q(t,c);if(!d.some(p=>!p.token))throw new Error("the connected wallet has no spendable coins");if(!bt(d)){i("preparing-wallet");let p=_t({ownerAddress:f,utxos:d,feeRate:a,addressToLockingBytecode:C}),y=await e.signTransaction(p,{userPrompt:"One-time setup so your wallet can create a name certificate"}),g=await Rt(t,e,y,i);i("prepared",{txid:g}),d=[{txid:g,vout:0,satoshis:p.transaction.outputs[0].valueSatoshis,address:f,lockingBytecode:C(f)},...d.filter(w=>!w.token&&!p.inputUtxos.some(x=>x.txid===w.txid&&x.vout===w.vout))]}i("building");let u=await Ht(t,{name:n,ownerAddress:f,records:s,utxos:d,serviceFee:r,feeRate:a});i("awaiting-approval");let h=await e.signTransaction(u,{userPrompt:`Register ${u.displayName} \u2014 the certificate is minted to your own address`}),m=await Rt(t,e,h,i);return i("registered",{txid:m,category:u.category}),{name:u.name,displayName:u.displayName,category:u.category,txid:m,ownerAddress:f,certificateAddress:j(f),costs:u.costs}}async function Ko(t,{addresses:e,category:n,rawName:o}){let s=await q(t,e),r=[...new TextEncoder().encode(o)].map(a=>a.toString(16).padStart(2,"0")).join("");return s.find(a=>a.token?.category===n&&a.token?.nft?.commitment===r)??null}async function Wo(t,{wallet:e,name:n,records:o,feeRate:s=L.feeRate,onProgress:r=()=>{}}){r("resolving");let a=D(n),l=(await pt({client:t})).get(z(a));if(!l)throw new Error(`"${z(a)}" is not registered`);r("loading-coins");let c=await q(t,e.watchedAddresses),f=c.find(m=>m.token?.category===l.category&&m.token?.nft);if(!f)throw new Error(`this wallet does not hold the certificate for "${z(a)}" \u2014 only the owner can change its records`);r("building");let d=Nt({name:n,ownerAddress:e.address,ownerTokenAddress:j(e.address),records:o,certificateUtxo:f,utxos:c.filter(m=>!m.token),beaconAddress:L.beacon,feeRate:s,addressToLockingBytecode:C});r("signing");let u=rt(d.transaction,d.sourceOutputs,m=>e.keyFor(m));r("broadcasting");let h=await tt(t,u.hex);return r("updated",{txid:h}),{name:d.name,displayName:d.displayName,category:d.category,txid:h,records:o,costs:d.costs}}async function pn(t,e,n=L.beacon){let o=await Xe(t,e.txid,!0),s=mt(C(n));return(o?.vout??[]).some(r=>r.scriptPubKey?.hex===s)}async function Go(t,{wallet:e,name:n,feeRate:o=L.feeRate,onProgress:s=()=>{}}){s("resolving");let r=D(n),i=(await pt({client:t})).get(z(r));if(!i)throw new Error(`"${z(r)}" is not registered`);s("loading-coins");let l=p=>p.token?.category===i.category&&p.token?.nft,c=await q(t,e.watchedAddresses),f=c.find(l);if(!f)throw new Error(`this wallet does not hold the certificate for "${z(r)}" \u2014 only the owner can release it`);let d=null;if(!await pn(t,f)){s("anchoring");let p=Nt({name:n,ownerAddress:e.address,ownerTokenAddress:j(e.address),records:i.records??{},certificateUtxo:f,utxos:c.filter(g=>!g.token),beaconAddress:L.beacon,feeRate:o,addressToLockingBytecode:C}),y=rt(p.transaction,p.sourceOutputs,g=>e.keyFor(g));d=await tt(t,y.hex),s("anchored",{txid:d}),f=null;for(let g=0;g<12&&!f;g++)await new Promise(w=>setTimeout(w,1500)),c=await q(t,e.watchedAddresses),f=c.find(w=>l(w)&&w.txid===d)??null;if(!f)throw new Error("the anchoring update was broadcast but is not visible yet \u2014 try the release again in a minute")}s("building");let u=fe({name:n,ownerAddress:e.address,certificateUtxo:f,utxos:c.filter(p=>!p.token),beaconAddress:L.beacon,feeRate:o,addressToLockingBytecode:C});s("signing");let h=rt(u.transaction,u.sourceOutputs,p=>e.keyFor(p));s("broadcasting");let m=await tt(t,h.hex);return s("released",{txid:m}),{name:u.name,displayName:u.displayName,category:u.category,txid:m,anchorTxid:d,costs:u.costs}}async function jo(t,{session:e,name:n,records:o,feeRate:s=L.feeRate,onProgress:r=()=>{}}){r("resolving");let a=D(n),l=(await pt({client:t})).get(z(a));if(!l)throw new Error(`"${z(a)}" is not registered`);r("reading-wallet");let c=await e.getAddresses(),f=await q(t,c),d=f.find(p=>p.token?.category===l.category&&p.token?.nft);if(!d)throw new Error(`this wallet does not hold the certificate for "${z(a)}" \u2014 only the owner can change its records`);r("building");let u=Nt({name:n,ownerAddress:d.address,ownerTokenAddress:j(d.address),records:o,certificateUtxo:d,utxos:f.filter(p=>!p.token),beaconAddress:L.beacon,feeRate:s,addressToLockingBytecode:C});r("awaiting-approval");let h=await e.signTransaction(u,{userPrompt:`Point ${u.displayName} somewhere new`}),m=await Rt(t,e,h,r);return r("updated",{txid:m}),{name:u.name,displayName:u.displayName,category:u.category,txid:m,records:o,costs:u.costs}}async function qo(t,{wallet:e,tld:n,records:o,category:s,feeRate:r=L.feeRate,onProgress:a=()=>{}}){if(!s)throw new Error("setTldRecords requires the TLD certificate's category id");a("loading-coins");let i=await q(t,e.watchedAddresses),l=i.find(u=>u.token?.category===s&&u.token?.nft);if(!l)throw new Error(`this wallet does not hold the TLD certificate for ".${n}" \u2014 only the owner can change its policy`);a("building");let c=Qe({tld:n,ownerAddress:e.address,ownerTokenAddress:j(e.address),records:o,certificateUtxo:l,utxos:i.filter(u=>!u.token),feeRate:r,addressToLockingBytecode:C});a("signing");let f=rt(c.transaction,c.sourceOutputs,u=>e.keyFor(u));a("broadcasting");let d=await tt(t,f.hex);return a("updated",{txid:d}),{tld:c.tld,displayName:c.displayName,category:c.category,txid:d,records:o,costs:c.costs}}async function zo(t,e){if(!e||e.v!==1&&e.v!==2)throw new Error("manifest.v must be 1 or 2");if(!e.name)throw new Error("manifest.name is required");if(typeof e.seq!="number"||e.seq<0)throw new Error("manifest.seq must be a non-negative integer");let n={...e};delete n.sig;let o=JSON.stringify(Se(n)),s=Bo.hash(Io(o)),r=t.signMessage(s);return{...e,sig:Po(r)}}function Se(t){if(t!=null){if(Array.isArray(t))return t.map(Se).filter(e=>e!==void 0);if(typeof t=="object"){let e={};for(let n of Object.keys(t).sort()){let o=Se(t[n]);o!==void 0&&(e[n]=o)}return e}return t}}async function Yo(t,e){let n=await q(t,e),o=n.filter(s=>!s.token);return{sats:o.reduce((s,r)=>s+r.satoshis,0n),confirmedSats:o.filter(s=>s.height>0).reduce((s,r)=>s+r.satoshis,0n),coins:o.length,certificates:n.filter(s=>s.token?.nft),hasGenesisInput:bt(n)}}import{hexToBin as Q,binToHex as nt,encodeDataPush as G,bigIntToVmNumber as Wt,hash256 as Te,hash160 as Vo,encodeLockingBytecodeP2sh32 as Xo,encodeLockingBytecodeP2pkh as wn,lockingBytecodeToCashAddress as Jo,OpcodesBCH as Qo,numberToBinUint32LE as lr,binToNumberUint32LE as yn,secp256k1 as xn,sha256 as Zo,generateSigningSerializationBCH as ts,encodeTransaction as bn,flattenBinArray as At}from"@bitauth/libauth";var gn={contractName:"UsdListing",constructorInputs:[{name:"oraclePk",type:"pubkey"},{name:"sellerPkh",type:"bytes20"},{name:"targetCents",type:"int"},{name:"floorSats",type:"int"},{name:"ceilSats",type:"int"},{name:"maxAgeSecs",type:"int"}],abi:[{name:"buy",inputs:[{name:"oracleMsg",type:"bytes"},{name:"oracleSig",type:"datasig"}]},{name:"cancel",inputs:[{name:"pk",type:"pubkey"},{name:"s",type:"sig"}]}],bytecode:"OP_6 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_TXLOCKTIME OP_CHECKLOCKTIMEVERIFY OP_DROP OP_7 OP_PICK OP_SIZE OP_NIP 28 OP_NUMEQUALVERIFY OP_8 OP_ROLL OP_8 OP_PICK OP_ROT OP_CHECKDATASIGVERIFY OP_6 OP_ROLL OP_4 OP_SPLIT OP_4 OP_SPLIT OP_ROT OP_BIN2NUM OP_ROT OP_BIN2NUM OP_DUP OP_0 OP_GREATERTHAN OP_VERIFY OP_ROT OP_INPUTINDEX OP_OUTPOINTTXHASH OP_EQUALVERIFY OP_TXLOCKTIME 302a OP_ADD OP_2 OP_PICK OP_GREATERTHANOREQUAL OP_VERIFY OP_TXLOCKTIME OP_ROT OP_7 OP_ROLL OP_ADD OP_LESSTHANOREQUAL OP_VERIFY OP_ROT 00e1f505 OP_MUL OP_SWAP OP_DIV OP_DUP OP_3 OP_PICK OP_LESSTHAN OP_IF OP_2 OP_PICK OP_NIP OP_ENDIF OP_DUP OP_4 OP_PICK OP_GREATERTHAN OP_IF OP_3 OP_PICK OP_NIP OP_ENDIF OP_0 OP_OUTPUTBYTECODE 76a914 OP_3 OP_ROLL OP_CAT 88ac OP_CAT OP_EQUALVERIFY OP_0 OP_OUTPUTVALUE OP_LESSTHANOREQUAL OP_VERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENCATEGORY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENCOMMITMENT OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_EQUAL OP_NIP OP_NIP OP_NIP OP_ELSE OP_6 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_6 OP_PICK OP_HASH160 OP_ROT OP_EQUALVERIFY OP_6 OP_ROLL OP_6 OP_ROLL OP_CHECKSIGVERIFY OP_2DROP OP_2DROP OP_DROP OP_1 OP_ENDIF",compiler:{name:"cashc",version:"0.13.3",options:{enforceFunctionParameterTypes:!0,enforceLocktimeGuard:!0}}};var es=2,Ee=1200,ns=7200,os=t=>{let e=Qo[t];if(e===void 0)throw new Error("unknown opcode "+t);return Uint8Array.of(e)};function ss(t){let e=t.trim().split(/\s+/).filter(Boolean).map(n=>n.startsWith("OP_")?os(n):G(Q(n)));return At(e)}var rs=ss(gn.bytecode),Kt=t=>G(Wt(BigInt(t))),is=0,mn=1;function as(t){let e=Q(t);if(e.length!==25||e[0]!==118||e[1]!==169||e[2]!==20)throw new Error("seller must hold the certificate on a plain P2PKH address");return nt(e.slice(3,23))}function Oe({oraclePk:t,sellerPkh:e,targetCents:n,floorSats:o,ceilSats:s,maxAgeSecs:r=Ee}){if(!(BigInt(n)>0n)||!(BigInt(o)>0n)||BigInt(s)<BigInt(o))throw new Error("bad listing terms");return At([Kt(r),Kt(s),Kt(o),Kt(n),G(Q(e)),G(Q(t)),rs])}var Gt=t=>Xo(Te(t));function _e(t,e=E.prefix){let n=Jo({bytecode:Gt(t),prefix:e});if(typeof n=="string")throw new Error(n);return n.address}function kn({targetCents:t,priceCents:e,floorSats:n,ceilSats:o}){let s=BigInt(t)*100000000n/BigInt(e);return s<BigInt(n)&&(s=BigInt(n)),s>BigInt(o)&&(s=BigInt(o)),s}var Sn=t=>({ts:yn(t.slice(0,4)),priceCents:yn(t.slice(4,8)),outpointTxid:nt(t.slice(8,40).slice().reverse())});function Tn(t,e,n){return xn.verifySignatureSchnorr(n,t,Zo.hash(e))===!0}var ve=(t,e,n,o,{requireGenesis:s=!1}={})=>{let r=F(t,e+600n,{requireGenesis:s});for(let a=0;a<6;a++){let{inputs:i,totalSats:l}=r,c=o(i,!0,1000000n),f=BigInt(Math.ceil(bn(c.transaction).length*n)),d=e+f;if(l<d){let m=F(t,d+200n,{requireGenesis:s});if(m.inputs.length>i.length){r=m;continue}let p=new Error(`not enough funds: need ${d} sat, have ${l} sat`);throw p.needSats=d,p.haveSats=l,p}let u=l-d,h=u>=K;return h||(u=0n),{...o(i,h,u),fee:h?f:l-e,change:u}}throw new Error("could not converge on a fee \u2014 please retry")};function cs({name:t,certificateUtxo:e,utxos:n,sellerAddress:o,oraclePk:s,targetCents:r,floorSats:a,ceilSats:i,maxAgeSecs:l=Ee,records:c={},beaconAddress:f=ct,feeRate:d=1,addressToLockingBytecode:u}){let h=D(t);if(!e?.token?.nft)throw new Error("certificateUtxo does not carry a certificate");let m=u(o),p=as(nt(u(e.address||o))),y=Oe({oraclePk:s,sellerPkh:p,targetCents:r,floorSats:a,ceilSats:i,maxAgeSecs:l}),g=Gt(y),w=J("UPD",h,c),x=n.filter(b=>!b.token),v=R+A-BigInt(e.satoshis);return{...ve(x,v>0n?v:0n,d,(b,B,T)=>({transaction:{version:2,locktime:0,inputs:[it(e),...b.map(S=>it(S))],outputs:[{lockingBytecode:g,valueSatoshis:R,token:ut(e.token)},{lockingBytecode:et(w),valueSatoshis:0n},{lockingBytecode:u(f),valueSatoshis:A},...B?[{lockingBytecode:m,valueSatoshis:T}]:[]]},sourceOutputs:[dt(e),...b.map(S=>dt(S))]})),name:h,redeem:y,redeemHex:nt(y),address:_e(y,o.split(":")[0]),sellerPkh:p,covenantVout:0}}function En(t){let e=Q(t.redeem_hex);return{redeem:e,utxo:{txid:t.outpoint.txid,vout:t.outpoint.vout,satoshis:BigInt(t.outpoint.satoshis??R),lockingBytecode:Gt(e),token:t.token}}}function ls({listing:t,quote:e,buyerAddress:n,buyerTokenAddress:o,utxos:s,records:r={},beaconAddress:a=ct,feeRate:i=1,addressToLockingBytecode:l}){let c=D(t.name),f=Q(e.message),d=Q(e.signature);if(!Tn(Q(t.oracle_pk),f,d))throw new Error("oracle signature does not verify against the listing's oracle key");let{ts:u,priceCents:h,outpointTxid:m}=Sn(f);if(m!==String(t.outpoint.txid).toLowerCase())throw new Error("quote was issued for a different listing");let p=kn({targetCents:t.target_cents,priceCents:h,floorSats:t.floor_sats,ceilSats:t.ceil_sats}),{redeem:y,utxo:g}=En(t),w=wn(Q(t.seller_pkh)),x=At([G(d),G(f),G(Wt(BigInt(is))),G(y)]),v=l(n),k=l(o??n),b=J("UPD",c,r),B=s.filter(P=>!P.token),T=p+R+A-g.satoshis;return{...ve(B,T,i,(P,I,O)=>({transaction:{version:2,locktime:Math.max(0,u-ns),inputs:[it(g,x),...P.map(_=>it(_))],outputs:[{lockingBytecode:w,valueSatoshis:p},{lockingBytecode:k,valueSatoshis:R,token:ut(g.token)},{lockingBytecode:et(b),valueSatoshis:0n},{lockingBytecode:l(a),valueSatoshis:A},...I?[{lockingBytecode:v,valueSatoshis:O}]:[]]},sourceOutputs:[dt(g),...P.map(_=>dt(_))]})),name:c,covenantInputIndex:0,priceSats:p,priceCents:h,quoteTs:u}}function us({listing:t,sellerAddress:e,sellerTokenAddress:n,utxos:o,records:s={},beaconAddress:r=ct,feeRate:a=1,addressToLockingBytecode:i,keyFor:l}){let c=D(t.name),{redeem:f,utxo:d}=En(t),u=wn(Q(t.seller_pkh)),h=l(nt(u));if(!h)throw new Error("this wallet does not hold the seller key for this listing");let m=h.privateKey,p=h.publicKey,y=i(n??e),g=J("UPD",c,s),w=o.filter(_=>!_.token),x=R+A-d.satoshis,v=At([G(new Uint8Array(65)),G(p),G(Wt(BigInt(mn))),G(f)]),k=ve(w,x>0n?x:0n,a,(_,U,N)=>({transaction:{version:2,locktime:0,inputs:[it(d,v),..._.map(M=>it(M))],outputs:[{lockingBytecode:y,valueSatoshis:R,token:ut(d.token)},{lockingBytecode:et(g),valueSatoshis:0n},{lockingBytecode:i(r),valueSatoshis:A},...U?[{lockingBytecode:i(e),valueSatoshis:N}]:[]]},sourceOutputs:[dt(d),..._.map(M=>dt(M))]})),b=vt(k.transaction,k.sourceOutputs,l,{skip:[0]}),B=ts({inputIndex:0,sourceOutputs:k.sourceOutputs,transaction:b.transaction},{coveredBytecode:f,signingSerializationType:Uint8Array.of($t)}),T=xn.signMessageHashSchnorr(m,Te(B));if(typeof T=="string")throw new Error(T);let S=At([T,Uint8Array.of($t)]),P=At([G(S),G(p),G(Wt(BigInt(mn))),G(f)]),I={...b.transaction,inputs:b.transaction.inputs.map((_,U)=>U===0?{..._,unlockingBytecode:P}:_)},O=bn(I);return{name:c,transaction:I,sourceOutputs:k.sourceOutputs,hex:nt(O),txid:nt(Te(O).slice().reverse())}}function ds(t,{oraclePk:e}={}){let n=o=>({ok:!1,error:o});try{if(t?.kind!=="usd"||t.v!==es)return n("not a v2 usd listing");for(let r of["name","seller","seller_pkh","category","target_cents","floor_sats","ceil_sats","max_age_secs","oracle_pk","redeem_hex","covenant_address","outpoint","token"])if(t[r]==null)return n("missing "+r);if(e&&t.oracle_pk!==e)return n("listing was made for a different oracle key");let o=Oe({oraclePk:t.oracle_pk,sellerPkh:t.seller_pkh,targetCents:t.target_cents,floorSats:t.floor_sats,ceilSats:t.ceil_sats,maxAgeSecs:t.max_age_secs});if(nt(o)!==t.redeem_hex)return n("redeem script does not match the stated terms");let s=String(t.seller).split(":")[0]||E.prefix;return _e(o,s)!==t.covenant_address?n("covenant address does not match"):nt(Vo(Q(t.seller_pkh))).length!==40?n("bad seller pkh"):t.token?.category!==t.category||!t.token?.nft?n("token does not match category"):Q(t.token.nft.commitment||"").length===0||nt(new TextEncoder().encode(D(t.name)))!==t.token.nft.commitment?n("commitment does not match the name"):{ok:!0,name:D(t.name),covenant_lock:nt(Gt(o))}}catch(o){return n(String(o?.message||o))}}import{hash160 as hs,lockingBytecodeToCashAddress as fs,binToHex as yr}from"@bitauth/libauth";function On(t){return Uint8Array.from([118,169,20,...hs(t),136,172])}function ps(t,e=E.prefix,n=!1){let o=fs({bytecode:On(t),prefix:e,tokenSupport:n});if(typeof o=="string")throw new Error(`address encoding failed: ${o}`);return o.address}function gs(t){return t?{ready:!0,approvals:1,explanation:"Your wallet can mint the certificate directly."}:{ready:!1,approvals:2,explanation:"Your wallet needs one small setup transaction to itself before it can create a certificate \u2014 a rule of the Bitcoin Cash token system, not a fee we charge. You will be asked to approve it, then the registration. This happens only once."}}import{binToHex as Nr,hexToBin as Rr}from"@bitauth/libauth";import{stringify as Cr}from"@bitauth/libauth";export{oe as BuiltInWallet,oo as CHIPNET_PREFIX,Ee as DEFAULT_MAX_AGE_SECS,Re as DEFAULT_NETWORK_ID,It as DEFAULT_PREFIX,X as InsufficientFundsError,so as MAINNET_PREFIX,E as NETWORK,Tt as NETWORKS,Ut as NoGenesisInputError,L as REGISTRAR,le as TLD_BEACON,C as addressToLockingBytecode,ho as addressToScripthash,Nr as binToHex,tt as broadcast,Do as buildCosignRequest,pt as buildIndex,To as buildListing,_t as buildPrepTx,he as buildRegistrationTx,fe as buildReleaseTx,Ft as buildTldRegistrationTx,Nt as buildUpdateTx,ls as buildUsdBuyTx,us as buildUsdCancelTx,cs as buildUsdListingTx,Mt as checkAvailability,_o as completeSale,Co as connect,gs as describeGenesisRequirement,z as displayName,Ko as findCertificateUtxo,hn as findTldCertificateUtxo,un as findTldOwnerAddress,Yo as getBalance,Et as getNetwork,Ve as getUtxos,q as getUtxosForAddresses,bt as hasGenesisInput,Rr as hexToBin,pn as isAnchored,D as normalizeName,Ot as normalizeTld,Sn as parseOracleMessage,ue as payloadBudget,kn as payoutSats,ko as priceSummary,ps as pubkeyToAddress,On as pubkeyToLockingBytecode,Ht as quoteRegistration,$o as registerTldWithBuiltInWallet,Mo as registerTldWithExternalWallet,Fo as registerWithBuiltInWallet,Ho as registerWithExternalWallet,Go as releaseWithBuiltInWallet,qt as requireBeacons,Xn as resolveName,Wo as setRecordsWithBuiltInWallet,jo as setRecordsWithExternalWallet,qo as setTldRecordsWithBuiltInWallet,Uo as signCosignRequest,vt as signInputs,zo as signRecordsManifest,rt as signTransaction,ke as sourceOutputFromJson,be as sourceOutputToJson,dn as splitServiceFee,Cr as stringify,j as toTokenAddress,Ze as toWcTransactionObject,ge as txidOf,ye as txidOfHex,_e as usdListingAddress,Oe as usdListingRedeem,cn as verifyListing,Tn as verifyOracleMessage,ds as verifyUsdListing};