A BCH-priced offer is a signed transaction with a fixed amount, so a seller who wants "$500" had to relist whenever the rate moved. The new USD mode moves the certificate into a small CashScript covenant (contracts/usd-listing.cash). Anyone may buy it by paying the seller target_cents / price, where the price comes from a message signed by the gateway's oracle key — the median of five exchanges the site already uses. The payout is clamped to a floor/ceiling band the seller sets, and the seller can reclaim the certificate at any time. What the chain can and cannot enforce, stated plainly in the contract and the UI: script can require a transaction to be mined no earlier than a time, never no later, so a quote cannot expire on-chain. The band is the guarantee, and quotes are bound to the listing's outpoint so cancelling and relisting voids every quote ever issued. An offline libauth VM test covers list, buy, underpayment, foreign-listing quotes, forged quotes, band clamping, cancel and a non-seller cancel; on chipnet aloevera.test was listed at $0.30, bought by a second wallet for 119,090 sat at $251.91, listed again by the new owner and cancelled. Gateway: GET /api/price/oracle[?txid=] serves the oracle public key and per-listing signed quotes; the key lives in oracle.key beside the gateway, never in the repo. POST /api/market accepts kind "usd" after reproducing the redeem script from the stated terms and finding the certificate in that covenant; such listings are pruned once the covenant output is spent; DELETE accepts the listing's seller while the index shows the covenant as holder. Dashboard: the Sell tab has "Price is fixed in: US dollars / BCH", a band control with the resulting BCH range, and cancel through the covenant. Market: USD listings show the dollar price, today's BCH equivalent and the band; buying fetches a quote and completes the covenant spend. Studio no longer sends a needless UPD when the registry view is stale, and missing files on a name now get a real 404 page.
7 lines
57 KiB
JavaScript
7 lines
57 KiB
JavaScript
import{generateBip39Mnemonic as dn,deriveSeedFromBip39Mnemonic as ye,deriveHdPrivateNodeFromSeed as hn,deriveHdPath as pn,secp256k1 as me,hash160 as fn,encodeCashAddress as Ko,lockingBytecodeToCashAddress as gn,cashAddressToLockingBytecode as Go,binToHex as yn,hexToBin as Wo,utf8ToBin as ke,binToBase64 as Ht,base64ToBin as Kt}from"@bitauth/libauth";var Et="bchtest";var Gt="m/44'/145'/0'",mn=5,At="bns.wallet.v1";function be(t){let e=fn(t);return Uint8Array.from([118,169,20,...e,136,172])}function we(t,e,n){let o=gn({bytecode:be(t),prefix:e,tokenSupport:n});if(typeof o=="string")throw new Error(`address encoding failed: ${o}`);return o.address}var Wt=class t{constructor(e,n,o=Et,s=Gt){this.mnemonic=e,this.prefix=o,this.accountPath=s,this._root=hn(n),this._keys=new Map,this._derive()}static async create(e=Et,n=Gt){let o=dn();return new t(o,await ye(o),e,n)}static async fromMnemonic(e,n=Et,o=Gt){let s=String(e).trim().toLowerCase().replace(/\s+/g," "),r=await ye(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<mn;o++){let s=`${this.accountPath}/${e}/${o}`,r=pn(this._root,s);if(typeof r=="string")throw new Error(`HD derivation failed at ${s}: ${r}`);let a=me.derivePublicKeyCompressed(r.privateKey);if(typeof a=="string")throw new Error(`pubkey derivation failed: ${a}`);let i=be(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(yn(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(h=>h.address===o);if(!s)throw new Error(`no key for address ${o}`);let r=me.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 xe(e,n),r=new Uint8Array(await crypto.subtle.encrypt({name:"AES-GCM",iv:o},s,ke(this.mnemonic)));return JSON.stringify({v:1,kdf:"PBKDF2-SHA256",iterations:qt,prefix:this.prefix,salt:Ht(n),iv:Ht(o),ct:Ht(r)})}static async decrypt(e,n){let o=typeof e=="string"?JSON.parse(e):e,s=await xe(n,Kt(o.salt),o.iterations??qt),r;try{r=await crypto.subtle.decrypt({name:"AES-GCM",iv:Kt(o.iv)},s,Kt(o.ct))}catch{throw new Error("wrong password")}return t.fromMnemonic(new TextDecoder().decode(r),o.prefix??Et)}async save(e,n=globalThis.localStorage){n.setItem(At,await this.encrypt(e))}static async load(e,n=globalThis.localStorage){let o=n.getItem(At);return o?t.decrypt(o,e):null}static exists(e=globalThis.localStorage){return!!e?.getItem(At)}static forget(e=globalThis.localStorage){e.removeItem(At)}},qt=25e4;async function xe(t,e,n=qt){let o=await crypto.subtle.importKey("raw",ke(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 Bt=["wss://silentmode.st/electrum","wss://chipnet.bch.ninja:50004","wss://chipnet.imaginary.cash:50004","wss://tbch4.loping.net:62002"],zt=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 Se(t=Bt,e){let n=[];for(let o of t)try{return await new zt(o,e).connect()}catch(s){n.push(`${o}: ${s?.message??s}`)}throw wn(t.length,n)}function wn(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 xn,lockingBytecodeToCashAddress as kn,sha256 as bn,hash160 as Yo,binToHex as Sn,hexToBin as Vo}from"@bitauth/libauth";function Te(t){return Sn(bn.hash(t).slice().reverse())}function L(t){let e=xn(t);if(typeof e=="string")throw new Error(`bad address "${t}": ${e}`);return e.bytecode}function Tn(t){return Te(L(t))}function W(t){let e=L(t),n=String(t).includes(":")?t.split(":")[0]:"bchtest",o=kn({bytecode:e,prefix:n,tokenSupport:!0});if(typeof o=="string")throw new Error(`cannot token-encode "${t}": ${o}`);return o.address}async function _e(t,e){let n=L(e),o=Te(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 z(t,e){let n=[];for(let o of e)n.push(...await _e(t,o));return n}async function ot(t,e){return t.call("blockchain.transaction.broadcast",[e])}import{binToHex as Pt,hexToBin as V,utf8ToBin as pt,binToUtf8 as Oe,encodeTransaction as _n,decodeTransaction as Qo}from"@bitauth/libauth";var On="BNS1",ut="bchtest:qp8njaa9a3ahg7259s5qajnce526n0rttsq8qwx4jl",Vt="bchtest:qzc4c76vtw9nd49g6x8nmvc0jz62uvk4dc7hmpmerh",v=600n,I=1000n,K=546n,jt="bch",Yt=200,En=/^[a-z0-9-]{1,32}$/,Ee=/^[a-z0-9-]{1,16}$/;function kt(t){let e=String(t).toLowerCase().trim().replace(/^\.+/,"").replace(/\.+$/,"");if(!Ee.test(e))throw new Error(`invalid TLD "${t}" (allowed: [a-z0-9-]{1,16})`);return e}var It=class extends Error{constructor(e){super(e),this.name="NoGenesisInputError",this.code="NO_GENESIS_INPUT"}},J=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 M(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=jt):(o=e[e.length-1],n=e[e.length-2]),!En.test(n)||!Ee.test(o))throw new Error(`invalid name "${t}" (allowed: <label>.<tld>, [a-z0-9-])`);return o===jt?n:`${n}.${o}`}function X(t){return t.includes(".")?t:`${t}.${jt}`}function Q(t,e,n){let o=JSON.stringify({o:t,n:e,r:n??{}}),s=pt(On+o);if(s.length>Yt)throw new Error(`records too large (${s.length} > ${Yt} bytes)`);return s}function Jt(t,e="REG"){return Yt-Q(e,t,{}).length}function vn(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 tt(t){return Uint8Array.from([106,...vn(t)])}function Xt(){return Uint8Array.from([65,...new Uint8Array(65),33,...new Uint8Array(33)])}var An=t=>!t.token;function $(t,e,{requireGenesis:n=!0}={}){let o=t.filter(An),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 It("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 ft(t){return{outpointTransactionHash:V(t.txid),outpointIndex:t.vout,sequenceNumber:4294967294,unlockingBytecode:Xt()}}function xt(t){return{...ft(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 lt(t){return _n(t).length}function Qt({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:h=ut,feeRate:u=1,addressToLockingBytecode:d,cosignUtxo:p=null,cosignTokenAddress:m=null}){if(typeof d!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let g=M(t),f=Q("REG",g,s),y=BigInt(i??0n),w=BigInt(c??0n);if(p&&!p.token?.nft)throw new Error("cosignUtxo does not carry a TLD certificate");let x=p?BigInt(p.satoshis):0n,_=p?d(m??n??e):null;if(y>0n&&!a)throw new Error("feeSats set but feeAddress missing");if(w>0n&&!l)throw new Error("tldFeeSats set but tldFeeAddress missing");let b=d(o??e),T=d(n??e),N=d(h),A=y>0n?d(a):null,k=w>0n?d(l):null,P=I+v+y+w+x,E=$(r,P+300n-x),O=0n,S=0n,C=!0;for(let B=0;B<6;B++){let{inputs:U}=E,F=p?[...U,p]:U,H=E.totalSats+x,at=F[0],nt=V(at.txid),q=(mt,wt)=>{let Y=[];return mt&&Y.push({lockingBytecode:b,valueSatoshis:wt}),Y.push({lockingBytecode:T,valueSatoshis:I,token:{amount:0n,category:nt,nft:{capability:"none",commitment:pt(g)}}}),Y.push({lockingBytecode:tt(f),valueSatoshis:0n}),Y.push({lockingBytecode:N,valueSatoshis:v}),k&&Y.push({lockingBytecode:k,valueSatoshis:w}),A&&Y.push({lockingBytecode:A,valueSatoshis:y}),p&&Y.push({lockingBytecode:_,valueSatoshis:x,token:{amount:BigInt(p.token.amount??0),category:V(p.token.category),nft:{capability:p.token.nft.capability,commitment:V(p.token.nft.commitment||"")}}}),{version:2,locktime:0,inputs:F.map(ft),outputs:Y}},cn=q(!0,1000000n),ln=lt(cn);O=BigInt(Math.ceil(ln*u));let Ot=P+O;if(H<Ot){let mt=$(r,Ot+200n-x);if(mt.inputs.length>U.length){E=mt;continue}throw new J(`not enough funds: need ${Ot} sat, have ${H} sat`,{needSats:Ot,haveSats:H})}if(S=H-Ot,C=S>=K,!C){let mt=q(!1,0n);O=H-P;let wt=BigInt(Math.ceil(lt(mt)*u));if(O<wt){let Y=$(r,P+wt+K+200n-x);if(Y.inputs.length>U.length){E=Y;continue}throw new J(`not enough funds: need ${P+wt} sat, have ${H} sat`,{needSats:P+wt,haveSats:H})}S=0n}let un=q(C,S),ct={},ht=0;return C&&(ct.change=ht++),ct.certificate=ht++,ct.opReturn=ht++,ct.beacon=ht++,k&&(ct.tldOwnerFee=ht++),A&&(ct.serviceFee=ht++),p&&(ct.cosignCertificate=ht++),{name:g,displayName:X(g),category:Pt(nt),transaction:un,sourceOutputs:F.map(xt),inputUtxos:F,outputMap:ct,payload:f,costs:{certificateDustSats:I,beaconDustSats:v,serviceFeeSats:y,tldOwnerFeeSats:w,chainFeeSats:O,changeSats:S,totalInputSats:H,netCostSats:y+w+O+v}}}throw new Error("could not converge on a fee \u2014 please retry")}function Nt({tld:t,ownerAddress:e,ownerTokenAddress:n,changeAddress:o,records:s={},utxos:r,feeAddress:a=null,feeSats:i=0n,beaconAddress:l=Vt,feeRate:c=1,addressToLockingBytecode:h}){if(typeof h!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let u=kt(t),d=Q("TREG",u,s),p=BigInt(i??0n);if(p>0n&&!a)throw new Error("feeSats set but feeAddress missing");let m=h(o??e),g=h(n??e),f=h(l),y=p>0n?h(a):null,w=I+v+p,x=$(r,w+300n),_=0n,b=0n,T=!0;for(let N=0;N<6;N++){let{inputs:A,totalSats:k}=x,P=A[0],E=V(P.txid),O=(at,nt)=>{let q=[];return at&&q.push({lockingBytecode:m,valueSatoshis:nt}),q.push({lockingBytecode:g,valueSatoshis:I,token:{amount:0n,category:E,nft:{capability:"none",commitment:pt(u)}}}),q.push({lockingBytecode:tt(d),valueSatoshis:0n}),q.push({lockingBytecode:f,valueSatoshis:v}),y&&q.push({lockingBytecode:y,valueSatoshis:p}),{version:2,locktime:0,inputs:A.map(ft),outputs:q}},S=O(!0,1000000n),C=lt(S);_=BigInt(Math.ceil(C*c));let B=w+_;if(k<B){let at=$(r,B+200n);if(at.inputs.length>A.length){x=at;continue}throw new J(`not enough funds: need ${B} sat, have ${k} sat`,{needSats:B,haveSats:k})}if(b=k-B,T=b>=K,!T){let at=O(!1,0n);_=k-w;let nt=BigInt(Math.ceil(lt(at)*c));if(_<nt){let q=$(r,w+nt+K+200n);if(q.inputs.length>A.length){x=q;continue}throw new J(`not enough funds: need ${w+nt} sat, have ${k} sat`,{needSats:w+nt,haveSats:k})}b=0n}let U=O(T,b),F={},H=0;return T&&(F.change=H++),F.certificate=H++,F.opReturn=H++,F.beacon=H++,y&&(F.serviceFee=H++),{tld:u,category:Pt(E),transaction:U,sourceOutputs:A.map(xt),inputUtxos:A,outputMap:F,payload:d,costs:{certificateDustSats:I,beaconDustSats:v,serviceFeeSats:p,chainFeeSats:_,changeSats:b,totalInputSats:k,netCostSats:p+_+v}}}throw new Error("could not converge on a fee \u2014 please retry")}function Lt({name:t,ownerAddress:e,ownerTokenAddress:n,records:o={},certificateUtxo:s,utxos:r,beaconAddress:a=ut,feeRate:i=1,addressToLockingBytecode:l}){if(typeof l!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let c=M(t);if(!s?.token?.nft)throw new Error("certificateUtxo does not carry a certificate");let h=Pt(pt(c));if(s.token.nft.commitment!==h)throw new Error(`certificate commitment mismatch: holds "${Oe(V(s.token.nft.commitment))}", asked to update "${c}"`);let u=Q("UPD",c,o),d=l(e),p=l(n??e),m=l(a),g=I+v,f=$(r,g+400n,{requireGenesis:!1}),y=0n,w=0n,x=!0;for(let _=0;_<6;_++){let b=[s,...f.inputs],T=s.satoshis+f.totalSats,N=(S,C)=>{let B=[];return S&&B.push({lockingBytecode:d,valueSatoshis:C}),B.push({lockingBytecode:p,valueSatoshis:I,token:{amount:0n,category:V(s.token.category),nft:{capability:"none",commitment:pt(c)}}}),B.push({lockingBytecode:tt(u),valueSatoshis:0n}),B.push({lockingBytecode:m,valueSatoshis:v}),{version:2,locktime:0,inputs:b.map(ft),outputs:B}},A=lt(N(!0,1000000n));y=BigInt(Math.ceil(A*i));let k=g+y;if(T<k){let S=$(r,k+200n,{requireGenesis:!1});if(S.inputs.length>f.inputs.length){f=S;continue}throw new J(`not enough funds: need ${k} sat, have ${T} sat`,{needSats:k,haveSats:T})}w=T-k,x=w>=K,x||(y=T-g,w=0n);let P=N(x,w),E={},O=0;return x&&(E.change=O++),E.certificate=O++,E.opReturn=O++,E.beacon=O++,{name:c,displayName:X(c),category:s.token.category,transaction:P,sourceOutputs:b.map(xt),inputUtxos:b,outputMap:E,payload:u,costs:{certificateDustSats:I,beaconDustSats:v,serviceFeeSats:0n,chainFeeSats:y,changeSats:w,totalInputSats:T,netCostSats:y+v}}}throw new Error("could not converge on a fee \u2014 please retry")}function ve({tld:t,ownerAddress:e,ownerTokenAddress:n,records:o={},certificateUtxo:s,utxos:r,beaconAddress:a=Vt,feeRate:i=1,addressToLockingBytecode:l}){if(typeof l!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let c=kt(t);if(!s?.token?.nft)throw new Error("certificateUtxo does not carry a certificate");let h=Pt(pt(c));if(s.token.nft.commitment!==h)throw new Error(`TLD certificate commitment mismatch: holds "${Oe(V(s.token.nft.commitment))}", asked to update ".${c}"`);let u=Q("TUPD",c,o),d=l(e),p=l(n??e),m=l(a),g=I+v,f=$(r,g+400n,{requireGenesis:!1}),y=0n,w=0n,x=!0;for(let _=0;_<6;_++){let b=[s,...f.inputs],T=s.satoshis+f.totalSats,N=(S,C)=>{let B=[];return S&&B.push({lockingBytecode:d,valueSatoshis:C}),B.push({lockingBytecode:p,valueSatoshis:I,token:{amount:0n,category:V(s.token.category),nft:{capability:"none",commitment:pt(c)}}}),B.push({lockingBytecode:tt(u),valueSatoshis:0n}),B.push({lockingBytecode:m,valueSatoshis:v}),{version:2,locktime:0,inputs:b.map(ft),outputs:B}},A=lt(N(!0,1000000n));y=BigInt(Math.ceil(A*i));let k=g+y;if(T<k){let S=$(r,k+200n,{requireGenesis:!1});if(S.inputs.length>f.inputs.length){f=S;continue}throw new J(`not enough funds: need ${k} sat, have ${T} sat`,{needSats:k,haveSats:T})}w=T-k,x=w>=K,x||(y=T-g,w=0n);let P=N(x,w),E={},O=0;return x&&(E.change=O++),E.certificate=O++,E.opReturn=O++,E.beacon=O++,{tld:c,displayName:`.${c}`,category:s.token.category,transaction:P,sourceOutputs:b.map(xt),inputUtxos:b,outputMap:E,payload:u,costs:{certificateDustSats:I,beaconDustSats:v,serviceFeeSats:0n,chainFeeSats:y,changeSats:w,totalInputSats:T,netCostSats:y+v}}}throw new Error("could not converge on a fee \u2014 please retry")}function bt({ownerAddress:t,utxos:e,amountSats:n=10000n,feeRate:o=1,addressToLockingBytecode:s}){let r=s(t),a=$(e,n+400n,{requireGenesis:!1});if(a.inputs.length===0)throw new J("wallet is empty");for(let i=0;i<6;i++){let{inputs:l,totalSats:c}=a,h=(m,g)=>({version:2,locktime:0,inputs:l.map(ft),outputs:[{lockingBytecode:r,valueSatoshis:n},...m?[{lockingBytecode:r,valueSatoshis:g}]:[]]}),u=BigInt(Math.ceil(lt(h(!0,1000000n))*o));if(c<n+u){let m=BigInt(Math.ceil(lt(h(!1,0n))*o));if(c<=m+K)throw new J(`not enough funds to prepare the wallet: have ${c} sat`,{needSats:m+K,haveSats:c});return{transaction:{version:2,locktime:0,inputs:l.map(ft),outputs:[{lockingBytecode:r,valueSatoshis:c-m}]},sourceOutputs:l.map(xt),inputUtxos:l,costs:{chainFeeSats:m}}}let d=c-n-u,p=d>=K;return{transaction:h(p,d),sourceOutputs:l.map(xt),inputUtxos:l,costs:{chainFeeSats:p?u:c-n}}}throw new Error("could not converge on a fee \u2014 please retry")}function gt(t){return t.some(e=>e.vout===0&&!e.token)}function Ae({transaction:t,sourceOutputs:e},{broadcast:n=!0,userPrompt:o}={}){return{transaction:t,sourceOutputs:e,broadcast:n,...o?{userPrompt:o}:{}}}import{generateSigningSerializationBCH as Bn,hash256 as Ie,secp256k1 as In,encodeTransaction as Pn,binToHex as Zt}from"@bitauth/libauth";var Be=65;function dt(t,e,n){let o={...t,inputs:t.inputs.map((r,a)=>{let i=e[a],l=n(Zt(i.lockingBytecode));if(!l)throw new Error(`no key for input ${a} \u2014 cannot sign`);let c=Bn({inputIndex:a,sourceOutputs:e,transaction:t},{coveredBytecode:i.lockingBytecode,signingSerializationType:Uint8Array.of(Be)}),h=Ie(c),u=In.signMessageHashSchnorr(l.privateKey,h);if(typeof u=="string")throw new Error(`signing failed on input ${a}: ${u}`);let d=Uint8Array.from([...u,Be]);return{...r,unlockingBytecode:Uint8Array.from([d.length,...d,l.publicKey.length,...l.publicKey])}})},s=Pn(o);return{transaction:o,hex:Zt(s),txid:te(s)}}function te(t){return Zt(Ie(t).slice().reverse())}function ee(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 te(e)}var Pe="e1cdbba719436f49acb4bf245b44df875c6d6b405eda46a9d3e736d138102f3b";var Ne="c85db10e106606273422b2733b2a6fb8b35a4d0e7dbfb20cf59bdb0833c008b8",Le="BNS1";var Nn=/^[a-z0-9-]{1,32}$/,Ce=/^[a-z0-9-]{1,16}$/,Ln=/^[a-z0-9-]{1,32}(\.[a-z0-9-]{1,16})?$/;function Rn(t){return t.includes(".")?t:t+".bch"}var Cn=[{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"}];var Dn=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},oe=t=>[...new TextEncoder().encode(t)].map(e=>e.toString(16).padStart(2,"0")).join("");function Un(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="bch"):(s=n[n.length-1],o=n[n.length-2]),!Nn.test(o)||!Ce.test(s))throw new Error(`invalid name "${t}" (allowed: <label>.<tld>, [a-z0-9-])`);return`${o}.${s}`}var Rt=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{}}},Fn=t=>typeof t=="string"?{url:t}:t;async function $n(t,e,{directIP:n=!1}={}){let o;for(let s of t){let r=Fn(s);try{return await new Rt(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 Rt(i,e,{servername:a.hostname}).connect()}catch(a){o=a}}throw o??new Error("no electrum server reachable")}function De(t){if(!t||!t.startsWith("6a"))return null;let e=Dn(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(Le))return null;try{let r=JSON.parse(s.slice(Le.length));if(Mn.has(r.o)&&typeof r.n=="string")return r}catch{}return null}var Mn=new Set(["REG","UPD","TREG","TUPD"]);function Ue(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 Fe(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(h=>s.has(h)));i<0&&(i=0);let[l]=a.splice(i,1);s.add(l.tx_hash),r.push(l)}return r}function ne(t){let e=t.lastIndexOf(".");return e<0?t:t.slice(e+1)}function Hn(t,e,n){let o=n.get(t);return o?o.regHeight>0&&e>0?o.regHeight<=e:!0:!1}async function se(t){let e=typeof t.unionHistory=="function"?(await t.unionHistory(Ne)).history:await t.call("blockchain.scripthash.get_history",[Ne]);e.sort(Ue);let n=new Map;for(let{tx_hash:s}of e)try{n.set(s,await t.call("blockchain.transaction.get",[s,!0]))}catch{}let o=new Map;for(let{tx_hash:s,height:r}of Fe(e.filter(a=>n.has(a.tx_hash)),n)){let i=n.get(s).vout||[],l=null;for(let h of i)l=De(h.scriptPubKey?.hex)??l;if(!l||typeof l.n!="string"||!Ce.test(l.n))continue;let c=l.n;if(l.o==="TREG"){if(o.has(c))continue;let h=oe(c),u=i.find(d=>d.tokenData?.nft&&d.tokenData.nft.commitment===h);if(!u)continue;o.set(c,{category:u.tokenData.category,regHeight:r,regTxid:s,mintScriptHex:u.scriptPubKey?.hex??null,records:l.r??{},updates:[{height:r,txid:s,records:l.r??{}}]})}else if(l.o==="TUPD"){let h=o.get(c);if(!h||!i.some(u=>u.tokenData&&u.tokenData.category===h.category))continue;h.records=l.r??{},h.updates.push({height:r,txid:s,records:h.records})}}return o}function Kn(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 Gn(t,e){let n=Kn(t,e);return n.policy==="cosign"||n.hidden===1||n.hidden===!0||n.hidden==="1"}function Wn(t,e,n){let o=oe(n);return(t||[]).some(s=>s.tokenData&&s.tokenData.category===e.category&&s.tokenData.nft&&s.tokenData.nft.commitment===o)}function Re(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}async function St({electrum:t=Cn,WebSocket:e,client:n,directIP:o=!1,enforceTld:s=!1,cosignGate:r=!0}={}){let a=n??await $n(t,e,{directIP:o});try{let i=null;if(s||r)try{i=await se(a)}catch(d){if(s)throw d;i=null}let l=null,c;if(typeof a.unionHistory=="function"){let d=await a.unionHistory(Pe);c=d.history,l={servers:d.servers,unreachable:d.unreachable,disagreements:d.disagreements}}else c=await a.call("blockchain.scripthash.get_history",[Pe]);c.sort(Ue);let h=new Map;for(let{tx_hash:d}of c)try{h.set(d,await a.call("blockchain.transaction.get",[d,!0]))}catch{}let u=new Map;for(let{tx_hash:d,height:p}of Fe(c.filter(m=>h.has(m.tx_hash)),h)){let g=h.get(d).vout||[],f=null;for(let x of g)f=De(x.scriptPubKey?.hex)??f;if(!f)continue;let y=f.n;if(!Ln.test(y))continue;let w=Rn(y);if(f.o==="REG"){if(u.has(w)||s&&!Hn(ne(w),p,i))continue;if(r&&i){let b=i.get(ne(w));if(b&&Gn(b,p)&&!Wn(g,b,ne(w)))continue}let x=oe(y),_=g.find(b=>b.tokenData?.nft&&b.tokenData.nft.commitment===x);if(!_)continue;u.set(w,{name:w,category:_.tokenData.category,records:f.r??{},txid:d,height:p,updatedTxid:null,owner:Re(_)})}else if(f.o==="UPD"){let x=u.get(w);if(!x)continue;let _=g.find(b=>b.tokenData&&b.tokenData.category===x.category);if(!_)continue;x.records=f.r??{},x.updatedTxid=d,x.owner=Re(_)??x.owner}}return l&&Object.defineProperty(u,"integrity",{value:l,enumerable:!1}),u}finally{n||a.close()}}async function qn(t,e={}){let n=Un(t);return(await St(e)).get(n)??null}var R={network:"chipnet",addressPrefix:"bchtest",selfHosted:null,get electrum(){return this.selfHosted?[this.selfHosted,...Bt]:Bt},beacon:"bchtest:qp8njaa9a3ahg7259s5qajnce526n0rttsq8qwx4jl",serviceFee:{address:"bchtest:qzew3dqcsj0guwe5y3uxf9q2xs5ajgy9lca4z093cu",sats:10000n},feeRate:1,wizardConnect:{dappName:"Silent Mode",dappIcon:"https://silentmode.st/icon.png"},faucets:[{name:"tbch.googol.cash",url:"https://tbch.googol.cash/"},{name:"chipnet.bch.ninja faucet",url:"https://chipnet.bch.ninja/faucet"}]};function zn(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 qe,binToHex as yt,decodeTransaction as Qn,lockingBytecodeToCashAddress as Zn,sha256 as to,utf8ToBin as eo,binToBase64 as no}from"@bitauth/libauth";import{encodeTransaction as ie,decodeTransaction as $e,generateSigningSerializationBCH as Me,hash256 as ae,hash160 as jn,secp256k1 as He,hexToBin as st,binToHex as D,utf8ToBin as Ke}from"@bitauth/libauth";var Ct=65,re=195,et=(t,e)=>({outpointTransactionHash:st(t.txid),outpointIndex:t.vout,sequenceNumber:4294967294,unlockingBytecode:e??Xt()}),rt=t=>t?{amount:BigInt(t.amount??0),category:st(t.category),...t.nft?{nft:{capability:t.nft.capability,commitment:st(t.nft.commitment||"")}}:{}}:void 0,ce=t=>({lockingBytecode:t.lockingBytecode instanceof Uint8Array?D(t.lockingBytecode):String(t.lockingBytecode),valueSatoshis:String(t.valueSatoshis),...t.token?{token:{category:t.token.category instanceof Uint8Array?D(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?D(t.token.nft.commitment):String(t.token.nft.commitment||"")}}:{}}}:{}}),le=t=>({lockingBytecode:st(t.lockingBytecode),valueSatoshis:BigInt(t.valueSatoshis??t.satoshis),...t.token?{token:rt(t.token)}:{}}),it=(t,e)=>({...et(t,e),lockingBytecode:t.lockingBytecode instanceof Uint8Array?t.lockingBytecode:st(t.lockingBytecode),valueSatoshis:BigInt(t.satoshis),...t.token?{token:rt(t.token)}:{}});function Ge(t,e,n,o,s){let r=e[n],a=Me({inputIndex:n,sourceOutputs:e,transaction:t},{coveredBytecode:r.lockingBytecode,signingSerializationType:Uint8Array.of(s)}),i=ae(a),l=He.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 Tt(t,e,n,{sighash:o=Ct,skip:s=[]}={}){let r={...t,inputs:t.inputs.map((i,l)=>{if(s.includes(l))return i;let c=n(D(e[l].lockingBytecode));return c?{...i,unlockingBytecode:Ge(t,e,l,c,o)}:i})},a=ie(r);return{transaction:r,hex:D(a),txid:D(ae(a).slice().reverse())}}function Yn({name:t,certificateUtxo:e,priceSats:n,sellerAddress:o,addressToLockingBytecode:s,keyFor:r}){let a=M(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!==D(Ke(a)))throw new Error("certificate commitment does not match the name");let l=s(o),c=it(e),h={version:2,locktime:0,inputs:[et(e)],outputs:[{lockingBytecode:l,valueSatoshis:i}]},u=r(D(c.lockingBytecode));if(!u)throw new Error("this wallet does not hold the key for the certificate");return h.inputs[0].unlockingBytecode=Ge(h,[c],0,u,re),{v:1,name:a,seller:o,price_sats:i.toString(),category:e.token.category,partial_tx:D(ie(h)),source_output:{txid:e.txid,vout:e.vout,satoshis:BigInt(e.satoshis).toString(),lockingBytecode:D(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 Vn(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 Jn=t=>Uint8Array.from([118,169,20,...jn(t),136,172]);function We(t,e={}){let n=o=>({ok:!1,error:o});try{if(!t||t.v!==1)return n("unsupported listing version");let o=M(t.name),s=$e(st(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(D(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!==D(Ke(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}=Vn(s.inputs[0].unlockingBytecode);if(i.length!==65||i[64]!==re)return n("input 0 is not signed SINGLE|ANYONECANPAY");let c=st(r.lockingBytecode);if(D(Jn(l))!==D(c))return n("public key does not own the certificate output");if(e.ownerLockingBytecode&&D(c)!==e.ownerLockingBytecode)return n("certificate is not held by the current owner");if(D(s.outputs[0].lockingBytecode)!==D(c))return n("payment does not go to the certificate holder");let h={...s.inputs[0],lockingBytecode:c,valueSatoshis:BigInt(r.satoshis),token:rt(r.token)},u=Me({inputIndex:0,sourceOutputs:[h],transaction:s},{coveredBytecode:c,signingSerializationType:Uint8Array.of(re)});return He.verifySignatureSchnorr(i.slice(0,64),l,ae(u))?{ok:!0,name:o,price_sats:a,seller_lock:D(c),category:r.token.category}:n("seller signature is invalid")}catch(o){return n(String(o?.message||o))}}function Xn({listing:t,buyerAddress:e,buyerTokenAddress:n,utxos:o,records:s={},beaconAddress:r=ut,feeRate:a=1,addressToLockingBytecode:i}){let l=We(t);if(!l.ok)throw new Error("bad listing: "+l.error);let c=l.name,h=$e(st(t.partial_tx)),u=t.source_output,d=h.inputs[0],p={...d,lockingBytecode:st(u.lockingBytecode),valueSatoshis:BigInt(u.satoshis),token:rt(u.token)},m=h.outputs[0],g=m.valueSatoshis,f=i(e),y=i(n??e),w=i(r),x=Q("UPD",c,s),_=g+I+v-BigInt(u.satoshis),b=$(o,_+400n,{requireGenesis:!1}),T=N=>ie(N).length;for(let N=0;N<6;N++){let{inputs:A,totalSats:k}=b,P=(U,F)=>({version:2,locktime:0,inputs:[d,...A.map(H=>et(H))],outputs:[m,{lockingBytecode:y,valueSatoshis:I,token:rt(u.token)},{lockingBytecode:tt(x),valueSatoshis:0n},{lockingBytecode:w,valueSatoshis:v},...U?[{lockingBytecode:f,valueSatoshis:F}]:[]]}),E=BigInt(Math.ceil(T(P(!0,1000000n))*a)),O=_+E;if(k<O){let U=$(o,O+200n,{requireGenesis:!1});if(U.inputs.length>A.length){b=U;continue}let F=new Error(`not enough funds: need ${O} sat, have ${k} sat`);throw F.needSats=O,F.haveSats=k,F}let S=k-O,C=S>=K;C||(S=0n);let B=P(C,S);return{name:c,transaction:B,sourceOutputs:[p,...A.map(U=>it(U))],sellerInputIndex:0,costs:{priceSats:g,beaconDustSats:v,certificateDustSats:I,chainFeeSats:C?E:k-_,changeSats:S,totalInputSats:k}}}throw new Error("could not converge on a fee \u2014 please retry")}var oo=90n,so=100n;async function ze(t,e,n=R.addressPrefix??"bchtest"){let s=(await se(t)).get(String(e).toLowerCase());if(!s?.mintScriptHex)return null;try{let r=qe(s.mintScriptHex),a=Zn({bytecode:r,prefix:n,tokenSupport:!1});return typeof a=="string"?null:a.address}catch{return null}}function je(t){let e=BigInt(t??0n),n=e*oo/so,o=e-n;return{tldOwnerFeeSats:n,platformFeeSats:o}}var ro=/silentmode\.st\/electrum|coinspectrum\.duckdns\.org:50011/;async function io({electrum:t=R.electrum,WebSocket:e,beaconOk:n=!1}={}){let o=n?t:t.filter(s=>!ro.test(typeof s=="string"?s:s.url||""));return Se(o,e)}async function Dt(t,e){let n=M(e),o=X(n),r=(await St({client:t})).get(o)??null;return{name:o,rawName:n,available:!r,entry:r,recordBudgetBytes:Jt(n)}}async function Ut(t,{name:e,ownerAddress:n,records:o={},utxos:s,serviceFee:r=R.serviceFee,feeRate:a=R.feeRate,cosignUtxo:i=null,cosignTokenAddress:l=null}){let c=s??await z(t,[n]),h=M(e),u=h.indexOf("."),d=u<0?"bch":h.slice(u+1),p=BigInt(r.sats??0n),m=null,g=0n,f=p;if(p>0n){try{m=await ze(t,d)}catch{m=null}if(m&&m!==n){let y=je(p);g=y.tldOwnerFeeSats,f=y.platformFeeSats}}return Qt({name:e,ownerAddress:n,ownerTokenAddress:W(n),records:o,utxos:c,feeAddress:f>0n?r.address:null,feeSats:f,tldFeeAddress:g>0n?m:null,tldFeeSats:g,beaconAddress:R.beacon,feeRate:a,addressToLockingBytecode:L,cosignUtxo:i,cosignTokenAddress:l})}function Ye(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 Ve(t,{wallet:e,utxos:n,feeRate:o,onProgress:s}){if(gt(n))return{utxos:n,prepTxid:null};s("preparing-wallet");let r=bt({ownerAddress:e.address,utxos:n,feeRate:o,addressToLockingBytecode:L}),a=dt(r.transaction,r.sourceOutputs,c=>e.keyFor(c)),i=await ot(t,a.hex);return s("prepared",{txid:i}),{utxos:a.transaction.outputs.slice(0,2).map((c,h)=>({txid:i,vout:h,satoshis:c.valueSatoshis,address:e.address,lockingBytecode:L(e.address)})),prepTxid:i}}async function ao(t,{wallet:e,name:n,records:o={},serviceFee:s=R.serviceFee,feeRate:r=R.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 Dt(t,n);if(!l.available)throw new Error(`"${l.name}" is already registered`);i("loading-coins");let c=await z(t,e.watchedAddresses);if(c.filter(f=>!f.token).reduce((f,y)=>f+y.satoshis,0n)===0n)throw new Error("wallet is empty \u2014 fund the address first");let u=null;({utxos:c,prepTxid:u}=await Ve(t,{wallet:e,utxos:c,feeRate:r,onProgress:i})),i("building");let d=await Ut(t,{name:n,ownerAddress:e.address,records:o,utxos:c,serviceFee:s,feeRate:r,cosignUtxo:{...a.utxo,address:a.owner},cosignTokenAddress:W(a.owner)}),p=d.transaction.inputs.length-1;i("signing");let m={...d.transaction,inputs:d.transaction.inputs.map((f,y)=>y===p?{...f,unlockingBytecode:new Uint8Array}:f)},g=Tt(m,d.sourceOutputs,f=>e.keyFor(f),{skip:[p]});return{name:d.name,displayName:d.displayName,category:d.category,tx:g.hex,sourceOutputs:d.sourceOutputs.map(ce),cosignInputIndex:p,costs:d.costs,prepTxid:u}}function co(t,e){let n=Qn(qe(String(t.tx||"")));if(typeof n=="string")throw new Error("invalid transaction: "+n);let o=(t.sourceOutputs||[]).map(le);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(yt(c.lockingBytecode))){s.push(l);return}if(c.token?.nft&&!n.outputs.some(u=>u.token&&yt(u.token.category)===yt(c.token.category)&&yt(u.token.nft?.commitment??new Uint8Array)===yt(c.token.nft.commitment??new Uint8Array)&&yt(u.lockingBytecode)===yt(c.lockingBytecode)&&u.valueSatoshis>=c.valueSatoshis))throw new Error("refusing to sign: the request would not return your TLD certificate to you");r+=1});let a=Tt(n,o,e,{skip:s});return{hex:a.hex,txid:a.txid,signedInputs:r}}async function lo(t,{wallet:e,name:n,records:o={},serviceFee:s=R.serviceFee,feeRate:r=R.feeRate,cosign:a=null,onProgress:i=()=>{}}){i("checking-availability");let l=await Dt(t,n);if(!l.available)throw new Error(`"${l.name}" is already registered`);i("loading-coins");let c=await z(t,e.watchedAddresses);if(c.filter(f=>!f.token).reduce((f,y)=>f+y.satoshis,0n)===0n)throw new Error("wallet is empty \u2014 fund the address first");let u=null;if(a&&(u=Ye(c,a),!u))throw new Error(`this wallet does not hold the certificate for .${a.tld} \u2014 only the TLD owner can co-sign`);let d=null;({utxos:c,prepTxid:d}=await Ve(t,{wallet:e,utxos:c,feeRate:r,onProgress:i})),i("building");let p=await Ut(t,{name:n,ownerAddress:e.address,records:o,utxos:c,serviceFee:s,feeRate:r,cosignUtxo:u,cosignTokenAddress:u?W(e.address):null});i("signing");let m=dt(p.transaction,p.sourceOutputs,f=>e.keyFor(f));i("broadcasting");let g=await ot(t,m.hex);return i("registered",{txid:g,category:p.category}),{name:p.name,displayName:p.displayName,category:p.category,txid:g,prepTxid:d,ownerAddress:e.address,certificateAddress:W(e.address),costs:p.costs,hex:m.hex}}async function uo(t,{wallet:e,tld:n,records:o={},serviceFee:s={address:null,sats:0n},feeRate:r=R.feeRate,tldListUrl:a=null,onProgress:i=()=>{}}){let l=kt(n);if(i("checking-availability"),a)try{let g=await fetch(a);if(g.ok){let f=await g.json();if(Array.isArray(f.tlds)&&f.tlds.some(w=>(w.tld||w)===l))throw new Error(`".${l}" is already registered as a TLD`)}}catch(g){if(/already registered/.test(g.message))throw g}i("loading-coins");let c=await z(t,e.watchedAddresses);if(c.filter(g=>!g.token).reduce((g,f)=>g+f.satoshis,0n)===0n)throw new Error("wallet is empty \u2014 fund the address first");let u=null;if(!gt(c)){i("preparing-wallet");let g=bt({ownerAddress:e.address,utxos:c,feeRate:r,addressToLockingBytecode:L}),f=dt(g.transaction,g.sourceOutputs,y=>e.keyFor(y));u=await ot(t,f.hex),i("prepared",{txid:u}),c=[{txid:u,vout:0,satoshis:f.transaction.outputs[0].valueSatoshis,address:e.address,lockingBytecode:L(e.address)},...f.transaction.outputs.length>1?[{txid:u,vout:1,satoshis:f.transaction.outputs[1].valueSatoshis,address:e.address,lockingBytecode:L(e.address)}]:[]]}i("building");let d=Nt({tld:l,ownerAddress:e.address,ownerTokenAddress:W(e.address),changeAddress:e.address,records:o,utxos:c,feeAddress:s.address||null,feeSats:BigInt(s.sats??0n),feeRate:r,addressToLockingBytecode:L});i("signing");let p=dt(d.transaction,d.sourceOutputs,g=>e.keyFor(g));i("broadcasting");let m=await ot(t,p.hex);return i("registered",{txid:m,category:d.category}),{tld:d.tld,category:d.category,txid:m,prepTxid:u,ownerAddress:e.address,certificateAddress:W(e.address),costs:d.costs,hex:p.hex}}async function ho(t,{session:e,tld:n,records:o={},serviceFee:s={address:null,sats:0n},feeRate:r=R.feeRate,tldListUrl:a=null,onProgress:i=()=>{}}){let l=kt(n);if(i("checking-availability"),a)try{let f=await fetch(a);if(f.ok){let y=await f.json();if(Array.isArray(y.tlds)&&y.tlds.some(x=>(x.tld||x)===l))throw new Error(`".${l}" is already registered as a TLD`)}}catch(f){if(/already registered/.test(f.message))throw f}i("reading-wallet");let c=await e.getAddresses();if(!c.length)throw new Error("the wallet exposed no addresses");let h=c[0];i("loading-coins");let u=await z(t,c);if(u.filter(f=>!f.token).reduce((f,y)=>f+y.satoshis,0n)===0n)throw new Error("the connected wallet has no spendable coins");let d=null;if(!gt(u)){i("preparing-wallet");let f=bt({ownerAddress:h,utxos:u,feeRate:r,addressToLockingBytecode:L}),y=await e.signTransaction(f,{userPrompt:"One-time setup so your wallet can create a TLD certificate"});d=await vt(t,e,y,i),i("prepared",{txid:d}),u=[{txid:d,vout:0,satoshis:f.transaction.outputs[0].valueSatoshis,address:h,lockingBytecode:L(h)},...u.filter(w=>!w.token&&!f.inputUtxos.some(x=>x.txid===w.txid&&x.vout===w.vout))]}i("building");let p=Nt({tld:l,ownerAddress:h,ownerTokenAddress:W(h),changeAddress:h,records:o,utxos:u,feeAddress:s.address||null,feeSats:BigInt(s.sats??0n),feeRate:r,addressToLockingBytecode:L});i("awaiting-approval");let m=await e.signTransaction(p,{userPrompt:`Register .${l} \u2014 the TLD certificate is minted to your own address`}),g=await vt(t,e,m,i);return i("registered",{txid:g,category:p.category}),{tld:p.tld,category:p.category,txid:g,prepTxid:d,ownerAddress:h,certificateAddress:W(h),costs:p.costs}}async function vt(t,e,n,o){let s=n.signedTransactionHash??ee(n.signedTransaction);if(e.walletBroadcasts)return s;o("broadcasting");try{return await ot(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 po(t,{session:e,name:n,ownerAddress:o,records:s={},serviceFee:r=R.serviceFee,feeRate:a=R.feeRate,onProgress:i=()=>{}}){i("checking-availability");let l=await Dt(t,n);if(!l.available)throw new Error(`"${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 h=o??c[0];i("loading-coins");let u=await z(t,c);if(!u.some(g=>!g.token))throw new Error("the connected wallet has no spendable coins");if(!gt(u)){i("preparing-wallet");let g=bt({ownerAddress:h,utxos:u,feeRate:a,addressToLockingBytecode:L}),f=await e.signTransaction(g,{userPrompt:"One-time setup so your wallet can create a name certificate"}),y=await vt(t,e,f,i);i("prepared",{txid:y}),u=[{txid:y,vout:0,satoshis:g.transaction.outputs[0].valueSatoshis,address:h,lockingBytecode:L(h)},...u.filter(w=>!w.token&&!g.inputUtxos.some(x=>x.txid===w.txid&&x.vout===w.vout))]}i("building");let d=await Ut(t,{name:n,ownerAddress:h,records:s,utxos:u,serviceFee:r,feeRate:a});i("awaiting-approval");let p=await e.signTransaction(d,{userPrompt:`Register ${d.displayName} \u2014 the certificate is minted to your own address`}),m=await vt(t,e,p,i);return i("registered",{txid:m,category:d.category}),{name:d.name,displayName:d.displayName,category:d.category,txid:m,ownerAddress:h,certificateAddress:W(h),costs:d.costs}}async function fo(t,{addresses:e,category:n,rawName:o}){let s=await z(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 go(t,{wallet:e,name:n,records:o,feeRate:s=R.feeRate,onProgress:r=()=>{}}){r("resolving");let a=M(n),l=(await St({client:t})).get(X(a));if(!l)throw new Error(`"${X(a)}" is not registered`);r("loading-coins");let c=await z(t,e.watchedAddresses),h=c.find(m=>m.token?.category===l.category&&m.token?.nft);if(!h)throw new Error(`this wallet does not hold the certificate for "${X(a)}" \u2014 only the owner can change its records`);r("building");let u=Lt({name:n,ownerAddress:e.address,ownerTokenAddress:W(e.address),records:o,certificateUtxo:h,utxos:c.filter(m=>!m.token),beaconAddress:R.beacon,feeRate:s,addressToLockingBytecode:L});r("signing");let d=dt(u.transaction,u.sourceOutputs,m=>e.keyFor(m));r("broadcasting");let p=await ot(t,d.hex);return r("updated",{txid:p}),{name:u.name,displayName:u.displayName,category:u.category,txid:p,records:o,costs:u.costs}}async function yo(t,{session:e,name:n,records:o,feeRate:s=R.feeRate,onProgress:r=()=>{}}){r("resolving");let a=M(n),l=(await St({client:t})).get(X(a));if(!l)throw new Error(`"${X(a)}" is not registered`);r("reading-wallet");let c=await e.getAddresses(),h=await z(t,c),u=h.find(g=>g.token?.category===l.category&&g.token?.nft);if(!u)throw new Error(`this wallet does not hold the certificate for "${X(a)}" \u2014 only the owner can change its records`);r("building");let d=Lt({name:n,ownerAddress:u.address,ownerTokenAddress:W(u.address),records:o,certificateUtxo:u,utxos:h.filter(g=>!g.token),beaconAddress:R.beacon,feeRate:s,addressToLockingBytecode:L});r("awaiting-approval");let p=await e.signTransaction(d,{userPrompt:`Point ${d.displayName} somewhere new`}),m=await vt(t,e,p,r);return r("updated",{txid:m}),{name:d.name,displayName:d.displayName,category:d.category,txid:m,records:o,costs:d.costs}}async function mo(t,{wallet:e,tld:n,records:o,category:s,feeRate:r=R.feeRate,onProgress:a=()=>{}}){if(!s)throw new Error("setTldRecords requires the TLD certificate's category id");a("loading-coins");let i=await z(t,e.watchedAddresses),l=i.find(d=>d.token?.category===s&&d.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=ve({tld:n,ownerAddress:e.address,ownerTokenAddress:W(e.address),records:o,certificateUtxo:l,utxos:i.filter(d=>!d.token),feeRate:r,addressToLockingBytecode:L});a("signing");let h=dt(c.transaction,c.sourceOutputs,d=>e.keyFor(d));a("broadcasting");let u=await ot(t,h.hex);return a("updated",{txid:u}),{tld:c.tld,displayName:c.displayName,category:c.category,txid:u,records:o,costs:c.costs}}async function wo(t,e){if(!e||e.v!==1)throw new Error("manifest.v must be 1");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(ue(n)),s=to.hash(eo(o)),r=t.signMessage(s);return{...e,sig:no(r)}}function ue(t){if(t!=null){if(Array.isArray(t))return t.map(ue).filter(e=>e!==void 0);if(typeof t=="object"){let e={};for(let n of Object.keys(t).sort()){let o=ue(t[n]);o!==void 0&&(e[n]=o)}return e}return t}}async function xo(t,e){let n=await z(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:gt(n)}}import{hexToBin as j,binToHex as Z,encodeDataPush as G,bigIntToVmNumber as $t,hash256 as de,hash160 as ko,encodeLockingBytecodeP2sh32 as bo,encodeLockingBytecodeP2pkh as Ze,lockingBytecodeToCashAddress as So,OpcodesBCH as To,numberToBinUint32LE as xs,binToNumberUint32LE as Xe,secp256k1 as tn,sha256 as _o,generateSigningSerializationBCH as Oo,encodeTransaction as en,flattenBinArray as _t}from"@bitauth/libauth";var Je={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 Eo=2,he=1200,vo=7200,Ao=t=>{let e=To[t];if(e===void 0)throw new Error("unknown opcode "+t);return Uint8Array.of(e)};function Bo(t){let e=t.trim().split(/\s+/).filter(Boolean).map(n=>n.startsWith("OP_")?Ao(n):G(j(n)));return _t(e)}var Io=Bo(Je.bytecode),Ft=t=>G($t(BigInt(t))),Po=0,Qe=1;function No(t){let e=j(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 Z(e.slice(3,23))}function pe({oraclePk:t,sellerPkh:e,targetCents:n,floorSats:o,ceilSats:s,maxAgeSecs:r=he}){if(!(BigInt(n)>0n)||!(BigInt(o)>0n)||BigInt(s)<BigInt(o))throw new Error("bad listing terms");return _t([Ft(r),Ft(s),Ft(o),Ft(n),G(j(e)),G(j(t)),Io])}var Mt=t=>bo(de(t));function fe(t,e="bchtest"){let n=So({bytecode:Mt(t),prefix:e});if(typeof n=="string")throw new Error(n);return n.address}function nn({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 on=t=>({ts:Xe(t.slice(0,4)),priceCents:Xe(t.slice(4,8)),outpointTxid:Z(t.slice(8,40).slice().reverse())});function sn(t,e,n){return tn.verifySignatureSchnorr(n,t,_o.hash(e))===!0}var ge=(t,e,n,o,{requireGenesis:s=!1}={})=>{let r=$(t,e+600n,{requireGenesis:s});for(let a=0;a<6;a++){let{inputs:i,totalSats:l}=r,c=o(i,!0,1000000n),h=BigInt(Math.ceil(en(c.transaction).length*n)),u=e+h;if(l<u){let m=$(t,u+200n,{requireGenesis:s});if(m.inputs.length>i.length){r=m;continue}let g=new Error(`not enough funds: need ${u} sat, have ${l} sat`);throw g.needSats=u,g.haveSats=l,g}let d=l-u,p=d>=K;return p||(d=0n),{...o(i,p,d),fee:p?h:l-e,change:d}}throw new Error("could not converge on a fee \u2014 please retry")};function Lo({name:t,certificateUtxo:e,utxos:n,sellerAddress:o,oraclePk:s,targetCents:r,floorSats:a,ceilSats:i,maxAgeSecs:l=he,records:c={},beaconAddress:h=ut,feeRate:u=1,addressToLockingBytecode:d}){let p=M(t);if(!e?.token?.nft)throw new Error("certificateUtxo does not carry a certificate");let m=d(o),g=No(Z(d(e.address||o))),f=pe({oraclePk:s,sellerPkh:g,targetCents:r,floorSats:a,ceilSats:i,maxAgeSecs:l}),y=Mt(f),w=Q("UPD",p,c),x=n.filter(T=>!T.token),_=I+v-BigInt(e.satoshis);return{...ge(x,_>0n?_:0n,u,(T,N,A)=>({transaction:{version:2,locktime:0,inputs:[et(e),...T.map(k=>et(k))],outputs:[{lockingBytecode:y,valueSatoshis:I,token:rt(e.token)},{lockingBytecode:tt(w),valueSatoshis:0n},{lockingBytecode:d(h),valueSatoshis:v},...N?[{lockingBytecode:m,valueSatoshis:A}]:[]]},sourceOutputs:[it(e),...T.map(k=>it(k))]})),name:p,redeem:f,redeemHex:Z(f),address:fe(f,o.split(":")[0]),sellerPkh:g,covenantVout:0}}function rn(t){let e=j(t.redeem_hex);return{redeem:e,utxo:{txid:t.outpoint.txid,vout:t.outpoint.vout,satoshis:BigInt(t.outpoint.satoshis??I),lockingBytecode:Mt(e),token:t.token}}}function Ro({listing:t,quote:e,buyerAddress:n,buyerTokenAddress:o,utxos:s,records:r={},beaconAddress:a=ut,feeRate:i=1,addressToLockingBytecode:l}){let c=M(t.name),h=j(e.message),u=j(e.signature);if(!sn(j(t.oracle_pk),h,u))throw new Error("oracle signature does not verify against the listing's oracle key");let{ts:d,priceCents:p,outpointTxid:m}=on(h);if(m!==String(t.outpoint.txid).toLowerCase())throw new Error("quote was issued for a different listing");let g=nn({targetCents:t.target_cents,priceCents:p,floorSats:t.floor_sats,ceilSats:t.ceil_sats}),{redeem:f,utxo:y}=rn(t),w=Ze(j(t.seller_pkh)),x=_t([G(u),G(h),G($t(BigInt(Po))),G(f)]),_=l(n),b=l(o??n),T=Q("UPD",c,r),N=s.filter(P=>!P.token),A=g+I+v-y.satoshis;return{...ge(N,A,i,(P,E,O)=>({transaction:{version:2,locktime:Math.max(0,d-vo),inputs:[et(y,x),...P.map(S=>et(S))],outputs:[{lockingBytecode:w,valueSatoshis:g},{lockingBytecode:b,valueSatoshis:I,token:rt(y.token)},{lockingBytecode:tt(T),valueSatoshis:0n},{lockingBytecode:l(a),valueSatoshis:v},...E?[{lockingBytecode:_,valueSatoshis:O}]:[]]},sourceOutputs:[it(y),...P.map(S=>it(S))]})),name:c,covenantInputIndex:0,priceSats:g,priceCents:p,quoteTs:d}}function Co({listing:t,sellerAddress:e,sellerTokenAddress:n,utxos:o,records:s={},beaconAddress:r=ut,feeRate:a=1,addressToLockingBytecode:i,keyFor:l}){let c=M(t.name),{redeem:h,utxo:u}=rn(t),d=Ze(j(t.seller_pkh)),p=l(Z(d));if(!p)throw new Error("this wallet does not hold the seller key for this listing");let m=p.privateKey,g=p.publicKey,f=i(n??e),y=Q("UPD",c,s),w=o.filter(S=>!S.token),x=I+v-u.satoshis,_=_t([G(new Uint8Array(65)),G(g),G($t(BigInt(Qe))),G(h)]),b=ge(w,x>0n?x:0n,a,(S,C,B)=>({transaction:{version:2,locktime:0,inputs:[et(u,_),...S.map(U=>et(U))],outputs:[{lockingBytecode:f,valueSatoshis:I,token:rt(u.token)},{lockingBytecode:tt(y),valueSatoshis:0n},{lockingBytecode:i(r),valueSatoshis:v},...C?[{lockingBytecode:i(e),valueSatoshis:B}]:[]]},sourceOutputs:[it(u),...S.map(U=>it(U))]})),T=Tt(b.transaction,b.sourceOutputs,l,{skip:[0]}),N=Oo({inputIndex:0,sourceOutputs:b.sourceOutputs,transaction:T.transaction},{coveredBytecode:h,signingSerializationType:Uint8Array.of(Ct)}),A=tn.signMessageHashSchnorr(m,de(N));if(typeof A=="string")throw new Error(A);let k=_t([A,Uint8Array.of(Ct)]),P=_t([G(k),G(g),G($t(BigInt(Qe))),G(h)]),E={...T.transaction,inputs:T.transaction.inputs.map((S,C)=>C===0?{...S,unlockingBytecode:P}:S)},O=en(E);return{name:c,transaction:E,sourceOutputs:b.sourceOutputs,hex:Z(O),txid:Z(de(O).slice().reverse())}}function Do(t,{oraclePk:e}={}){let n=o=>({ok:!1,error:o});try{if(t?.kind!=="usd"||t.v!==Eo)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=pe({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(Z(o)!==t.redeem_hex)return n("redeem script does not match the stated terms");let s=String(t.seller).split(":")[0]||"bchtest";return fe(o,s)!==t.covenant_address?n("covenant address does not match"):Z(ko(j(t.seller_pkh))).length!==40?n("bad seller pkh"):t.token?.category!==t.category||!t.token?.nft?n("token does not match category"):j(t.token.nft.commitment||"").length===0||Z(new TextEncoder().encode(M(t.name)))!==t.token.nft.commitment?n("commitment does not match the name"):{ok:!0,name:M(t.name),covenant_lock:Z(Mt(o))}}catch(o){return n(String(o?.message||o))}}import{hash160 as Uo,lockingBytecodeToCashAddress as Fo,binToHex as Os}from"@bitauth/libauth";function an(t){return Uint8Array.from([118,169,20,...Uo(t),136,172])}function $o(t,e="bchtest",n=!1){let o=Fo({bytecode:an(t),prefix:e,tokenSupport:n});if(typeof o=="string")throw new Error(`address encoding failed: ${o}`);return o.address}function Mo(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 Ms,hexToBin as Hs}from"@bitauth/libauth";import{stringify as Gs}from"@bitauth/libauth";export{Wt as BuiltInWallet,Et as CHIPNET_PREFIX,he as DEFAULT_MAX_AGE_SECS,J as InsufficientFundsError,It as NoGenesisInputError,R as REGISTRAR,Vt as TLD_BEACON,L as addressToLockingBytecode,Tn as addressToScripthash,Ms as binToHex,ot as broadcast,ao as buildCosignRequest,St as buildIndex,Yn as buildListing,bt as buildPrepTx,Qt as buildRegistrationTx,Nt as buildTldRegistrationTx,Lt as buildUpdateTx,Ro as buildUsdBuyTx,Co as buildUsdCancelTx,Lo as buildUsdListingTx,Dt as checkAvailability,Xn as completeSale,io as connect,Mo as describeGenesisRequirement,X as displayName,fo as findCertificateUtxo,Ye as findTldCertificateUtxo,ze as findTldOwnerAddress,xo as getBalance,_e as getUtxos,z as getUtxosForAddresses,gt as hasGenesisInput,Hs as hexToBin,M as normalizeName,kt as normalizeTld,on as parseOracleMessage,Jt as payloadBudget,nn as payoutSats,zn as priceSummary,$o as pubkeyToAddress,an as pubkeyToLockingBytecode,Ut as quoteRegistration,uo as registerTldWithBuiltInWallet,ho as registerTldWithExternalWallet,lo as registerWithBuiltInWallet,po as registerWithExternalWallet,qn as resolveName,go as setRecordsWithBuiltInWallet,yo as setRecordsWithExternalWallet,mo as setTldRecordsWithBuiltInWallet,co as signCosignRequest,Tt as signInputs,wo as signRecordsManifest,dt as signTransaction,le as sourceOutputFromJson,ce as sourceOutputToJson,je as splitServiceFee,Gs as stringify,W as toTokenAddress,Ae as toWcTransactionObject,te as txidOf,ee as txidOfHex,fe as usdListingAddress,pe as usdListingRedeem,We as verifyListing,sn as verifyOracleMessage,Do as verifyUsdListing};
|