Snapshot of the decentralized-web stack at the point of the resolver+Theseus rebuild deploy. Includes: - Argus (BNS engine + resolver daemon + Sia gateway) - AriadneResolver (Windows Inno installer bundle + Android APK sources + Firefox extension) - TheseusNavigator (Electron browser) - site/ (silentmode.st content, deployed to Sia at bns/silentmode/) - design docs, roadmap, protocol spec Secrets excluded via .gitignore: Argus/sia-s3.json, Argus/wallets.json, Argus/ca/*.key,*.crt. Build outputs, node_modules, and bundled runtimes also excluded. Shipped hashes on dl.silentmode.st at this commit: AriadneResolver-Setup-0.1.0.exe 5bcb216eef31ea28ed767e4134ab74bd5ac69dfbd365fd249e9e6938e55c986a TheseusNavigator-Setup-0.0.1.exe 7c735e88bad2da3347145adba3016c8f626a18b8422289c8c6ba471972e2952b TheseusNavigator-0.0.1-portable.exe 008fd84445babeabb401b2bca40ea9466b24b0e6d6c85104da7640c5c5c84521 ariadne-v0.2.apk 635c8f04d44ef855a8390b9eeddb8cd2d50622e81cc4e5004e8daffc1bb0425c
5 lines
26 KiB
JavaScript
5 lines
26 KiB
JavaScript
import{generateBip39Mnemonic as Mt,deriveSeedFromBip39Mnemonic as At,deriveHdPrivateNodeFromSeed as Kt,deriveHdPath as qt,secp256k1 as jt,hash160 as Gt,encodeCashAddress as He,lockingBytecodeToCashAddress as zt,cashAddressToLockingBytecode as We,binToHex as Pt,hexToBin as Me,utf8ToBin as Nt,binToBase64 as ct,base64ToBin as lt}from"@bitauth/libauth";var X="bchtest";var Jt="m/44'/145'/0'",Yt=5,nt="bns.wallet.v1";function It(e){let t=Gt(e);return Uint8Array.from([118,169,20,...t,136,172])}function Bt(e,t,n){let o=zt({bytecode:It(e),prefix:t,tokenSupport:n});if(typeof o=="string")throw new Error(`address encoding failed: ${o}`);return o.address}var dt=class e{constructor(t,n,o=X){this.mnemonic=t,this.prefix=o,this._root=Kt(n),this._keys=new Map,this._derive()}static async create(t=X){let n=Mt();return new e(n,await At(n),t)}static async fromMnemonic(t,n=X){let o=String(t).trim().toLowerCase().replace(/\s+/g," "),s=await At(o);if(typeof s=="string")throw new Error(`invalid recovery phrase: ${s}`);return new e(o,s,n)}_derive(){this.receive=[],this.change=[];for(let[t,n]of[[0,this.receive],[1,this.change]])for(let o=0;o<Yt;o++){let s=`${Jt}/${t}/${o}`,r=qt(this._root,s);if(typeof r=="string")throw new Error(`HD derivation failed at ${s}: ${r}`);let a=jt.derivePublicKeyCompressed(r.privateKey);if(typeof a=="string")throw new Error(`pubkey derivation failed: ${a}`);let i=It(a),c={path:s,branch:t,index:o,privateKey:r.privateKey,publicKey:a,lockingBytecode:i,address:Bt(a,this.prefix,!1),tokenAddress:Bt(a,this.prefix,!0)};n.push(c),this._keys.set(Pt(i),c)}}get address(){return this.receive[0].address}get tokenAddress(){return this.receive[0].tokenAddress}get watchedAddresses(){return[...this.receive,...this.change].map(t=>t.address)}keyFor(t){let n=this._keys.get(t);return n?{privateKey:n.privateKey,publicKey:n.publicKey}:null}async encrypt(t){let n=crypto.getRandomValues(new Uint8Array(16)),o=crypto.getRandomValues(new Uint8Array(12)),s=await _t(t,n),r=new Uint8Array(await crypto.subtle.encrypt({name:"AES-GCM",iv:o},s,Nt(this.mnemonic)));return JSON.stringify({v:1,kdf:"PBKDF2-SHA256",iterations:ht,prefix:this.prefix,salt:ct(n),iv:ct(o),ct:ct(r)})}static async decrypt(t,n){let o=typeof t=="string"?JSON.parse(t):t,s=await _t(n,lt(o.salt),o.iterations??ht),r;try{r=await crypto.subtle.decrypt({name:"AES-GCM",iv:lt(o.iv)},s,lt(o.ct))}catch{throw new Error("wrong password")}return e.fromMnemonic(new TextDecoder().decode(r),o.prefix??X)}async save(t,n=globalThis.localStorage){n.setItem(nt,await this.encrypt(t))}static async load(t,n=globalThis.localStorage){let o=n.getItem(nt);return o?e.decrypt(o,t):null}static exists(t=globalThis.localStorage){return!!t?.getItem(nt)}static forget(t=globalThis.localStorage){t.removeItem(nt)}},ht=25e4;async function _t(e,t,n=ht){let o=await crypto.subtle.importKey("raw",Nt(String(e)),"PBKDF2",!1,["deriveKey"]);return crypto.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:n,hash:"SHA-256"},o,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}var pt=["wss://chipnet.bch.ninja:50004","wss://chipnet.imaginary.cash:50004","wss://tbch4.loping.net:62002"],ut=class{constructor(t,n){this.url=t,this.WS=n||globalThis.WebSocket,this.id=0,this.pending=new Map,this.buf=""}connect(){return new Promise((t,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"]),t(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(t){let n=this.pending.get(t.id);n&&(this.pending.delete(t.id),t.error?n.reject(new Error(JSON.stringify(t.error))):n.resolve(t.result))}_onData(t){this.buf+=t;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(t,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:t,params:n})+`
|
|
`),setTimeout(()=>{this.pending.has(o)&&(this.pending.delete(o),r(new Error(`electrum timeout: ${t}`)))},2e4)})}close(){try{this.ws.close()}catch{}}};async function $t(e=pt,t){let n;for(let o of e)try{return await new ut(o,t).connect()}catch(s){n=s}throw n??new Error("no electrum server reachable")}import{cashAddressToLockingBytecode as Xt,lockingBytecodeToCashAddress as Vt,sha256 as Qt,binToHex as Zt,hexToBin as Ge}from"@bitauth/libauth";function te(e){return Zt(Qt.hash(e).slice().reverse())}function S(e){let t=Xt(e);if(typeof t=="string")throw new Error(`bad address "${e}": ${t}`);return t.bytecode}function F(e){let t=S(e),n=String(e).includes(":")?e.split(":")[0]:"bchtest",o=Vt({bytecode:t,prefix:n,tokenSupport:!0});if(typeof o=="string")throw new Error(`cannot token-encode "${e}": ${o}`);return o.address}async function Rt(e,t){let n=S(t),o=te(n);return(await e.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:t,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 _(e,t){let n=[];for(let o of t)n.push(...await Rt(e,o));return n}async function q(e,t){return e.call("blockchain.transaction.broadcast",[t])}import{binToHex as Ut,hexToBin as P,utf8ToBin as ot,binToUtf8 as ee,encodeTransaction as ne,decodeTransaction as Je}from"@bitauth/libauth";var oe="BNS1",Dt="bchtest:qp8njaa9a3ahg7259s5qajnce526n0rttsq8qwx4jl",$=600n,j=1000n,G=546n,ft="bch",gt=200,se=/^[a-z0-9-]{1,32}$/,re=/^[a-z0-9-]{1,16}$/;var st=class extends Error{constructor(t){super(t),this.name="NoGenesisInputError",this.code="NO_GENESIS_INPUT"}},R=class extends Error{constructor(t,{needSats:n,haveSats:o}={}){super(t),this.name="InsufficientFundsError",this.code="INSUFFICIENT_FUNDS",this.needSats=n,this.haveSats=o}};function O(e){let t=String(e).toLowerCase().trim().replace(/\.$/,"").split(".").filter(Boolean),n,o;if(t.length===0)throw new Error(`invalid name "${e}"`);if(t.length===1?(n=t[0],o=ft):(o=t[t.length-1],n=t[t.length-2]),!se.test(n)||!re.test(o))throw new Error(`invalid name "${e}" (allowed: <label>.<tld>, [a-z0-9-])`);return o===ft?n:`${n}.${o}`}function k(e){return e.includes(".")?e:`${e}.${ft}`}function yt(e,t,n){let o=JSON.stringify({o:e,n:t,r:n??{}}),s=ot(oe+o);if(s.length>gt)throw new Error(`records too large (${s.length} > ${gt} bytes)`);return s}function wt(e,t="REG"){return gt-yt(t,e,{}).length}function ie(e){return e.length<=75?Uint8Array.from([e.length,...e]):e.length<=255?Uint8Array.from([76,e.length,...e]):Uint8Array.from([77,e.length&255,e.length>>8&255,...e])}function Ct(e){return Uint8Array.from([106,...ie(e)])}function ae(){return Uint8Array.from([65,...new Uint8Array(65),33,...new Uint8Array(33)])}var ce=e=>!e.token;function z(e,t,{requireGenesis:n=!0}={}){let o=e.filter(ce),s=[],r=0n;if(n){let i=o.filter(c=>c.vout===0).sort((c,d)=>d.satoshis>c.satoshis?1:-1);if(i.length===0)throw new st("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(c=>c.txid===i.txid&&c.vout===i.vout)).sort((i,c)=>c.satoshis>i.satoshis?1:-1);for(let i of a){if(r>=t)break;s.push(i),r+=i.satoshis}return{inputs:s,totalSats:r}}function V(e){return{outpointTransactionHash:P(e.txid),outpointIndex:e.vout,sequenceNumber:4294967294,unlockingBytecode:ae()}}function rt(e){return{...V(e),lockingBytecode:e.lockingBytecode,valueSatoshis:e.satoshis,...e.token?{token:{amount:e.token.amount??0n,category:P(e.token.category),...e.token.nft?{nft:{capability:e.token.nft.capability,commitment:P(e.token.nft.commitment||"")}}:{}}}:{}}}function Q(e){return ne(e).length}function mt({name:e,ownerAddress:t,ownerTokenAddress:n,records:o={},utxos:s,feeAddress:r=null,feeSats:a=0n,beaconAddress:i=Dt,feeRate:c=1,addressToLockingBytecode:d}){if(typeof d!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let p=O(e),l=yt("REG",p,o),u=BigInt(a??0n);if(u>0n&&!r)throw new Error("feeSats set but feeAddress missing");let g=d(t),h=d(n??t),f=d(i),y=u>0n?d(r):null,w=j+$+u,b=z(s,w+300n),m=0n,N=0n,I=!0;for(let T=0;T<6;T++){let{inputs:E,totalSats:v}=b,U=E[0],et=P(U.txid),A=(M,K)=>{let B=[];return M&&B.push({lockingBytecode:g,valueSatoshis:K}),B.push({lockingBytecode:h,valueSatoshis:j,token:{amount:0n,category:et,nft:{capability:"none",commitment:ot(p)}}}),B.push({lockingBytecode:Ct(l),valueSatoshis:0n}),B.push({lockingBytecode:f,valueSatoshis:$}),y&&B.push({lockingBytecode:y,valueSatoshis:u}),{version:2,locktime:0,inputs:E.map(V),outputs:B}},L=A(!0,1000000n),H=Q(L);m=BigInt(Math.ceil(H*c));let D=w+m;if(v<D){let M=z(s,D+200n);if(M.inputs.length>E.length){b=M;continue}throw new R(`not enough funds: need ${D} sat, have ${v} sat`,{needSats:D,haveSats:v})}if(N=v-D,I=N>=G,!I){let M=A(!1,0n);m=v-w;let K=BigInt(Math.ceil(Q(M)*c));if(m<K){let B=z(s,w+K+G+200n);if(B.inputs.length>E.length){b=B;continue}throw new R(`not enough funds: need ${w+K} sat, have ${v} sat`,{needSats:w+K,haveSats:v})}N=0n}let C=A(I,N),W={},Y=0;return I&&(W.change=Y++),W.certificate=Y++,W.opReturn=Y++,W.beacon=Y++,y&&(W.serviceFee=Y++),{name:p,displayName:k(p),category:Ut(et),transaction:C,sourceOutputs:E.map(rt),inputUtxos:E,outputMap:W,payload:l,costs:{certificateDustSats:j,beaconDustSats:$,serviceFeeSats:u,chainFeeSats:m,changeSats:N,totalInputSats:v,netCostSats:u+m+$}}}throw new Error("could not converge on a fee \u2014 please retry")}function it({name:e,ownerAddress:t,ownerTokenAddress:n,records:o={},certificateUtxo:s,utxos:r,beaconAddress:a=Dt,feeRate:i=1,addressToLockingBytecode:c}){if(typeof c!="function")throw new Error("addressToLockingBytecode must be injected (see chain.js)");let d=O(e);if(!s?.token?.nft)throw new Error("certificateUtxo does not carry a certificate");let p=Ut(ot(d));if(s.token.nft.commitment!==p)throw new Error(`certificate commitment mismatch: holds "${ee(P(s.token.nft.commitment))}", asked to update "${d}"`);let l=yt("UPD",d,o),u=c(t),g=c(n??t),h=c(a),f=j+$,y=z(r,f+400n,{requireGenesis:!1}),w=0n,b=0n,m=!0;for(let N=0;N<6;N++){let I=[s,...y.inputs],T=s.satoshis+y.totalSats,E=(H,D)=>{let C=[];return H&&C.push({lockingBytecode:u,valueSatoshis:D}),C.push({lockingBytecode:g,valueSatoshis:j,token:{amount:0n,category:P(s.token.category),nft:{capability:"none",commitment:ot(d)}}}),C.push({lockingBytecode:Ct(l),valueSatoshis:0n}),C.push({lockingBytecode:h,valueSatoshis:$}),{version:2,locktime:0,inputs:I.map(V),outputs:C}},v=Q(E(!0,1000000n));w=BigInt(Math.ceil(v*i));let U=f+w;if(T<U){let H=z(r,U+200n,{requireGenesis:!1});if(H.inputs.length>y.inputs.length){y=H;continue}throw new R(`not enough funds: need ${U} sat, have ${T} sat`,{needSats:U,haveSats:T})}b=T-U,m=b>=G,m||(w=T-f,b=0n);let et=E(m,b),A={},L=0;return m&&(A.change=L++),A.certificate=L++,A.opReturn=L++,A.beacon=L++,{name:d,displayName:k(d),category:s.token.category,transaction:et,sourceOutputs:I.map(rt),inputUtxos:I,outputMap:A,payload:l,costs:{certificateDustSats:j,beaconDustSats:$,serviceFeeSats:0n,chainFeeSats:w,changeSats:b,totalInputSats:T,netCostSats:w+$}}}throw new Error("could not converge on a fee \u2014 please retry")}function at({ownerAddress:e,utxos:t,amountSats:n=10000n,feeRate:o=1,addressToLockingBytecode:s}){let r=s(e),a=z(t,n+400n,{requireGenesis:!1});if(a.inputs.length===0)throw new R("wallet is empty");for(let i=0;i<6;i++){let{inputs:c,totalSats:d}=a,p=(h,f)=>({version:2,locktime:0,inputs:c.map(V),outputs:[{lockingBytecode:r,valueSatoshis:n},...h?[{lockingBytecode:r,valueSatoshis:f}]:[]]}),l=BigInt(Math.ceil(Q(p(!0,1000000n))*o));if(d<n+l){let h=BigInt(Math.ceil(Q(p(!1,0n))*o));if(d<=h+G)throw new R(`not enough funds to prepare the wallet: have ${d} sat`,{needSats:h+G,haveSats:d});return{transaction:{version:2,locktime:0,inputs:c.map(V),outputs:[{lockingBytecode:r,valueSatoshis:d-h}]},sourceOutputs:c.map(rt),inputUtxos:c,costs:{chainFeeSats:h}}}let u=d-n-l,g=u>=G;return{transaction:p(g,u),sourceOutputs:c.map(rt),inputUtxos:c,costs:{chainFeeSats:g?l:d-n}}}throw new Error("could not converge on a fee \u2014 please retry")}function Z(e){return e.some(t=>t.vout===0&&!t.token)}function Ft({transaction:e,sourceOutputs:t},{broadcast:n=!0,userPrompt:o}={}){return{transaction:e,sourceOutputs:t,broadcast:n,...o?{userPrompt:o}:{}}}import{generateSigningSerializationBCH as le,hash256 as Lt,secp256k1 as de,encodeTransaction as he,binToHex as xt}from"@bitauth/libauth";var Ot=65;function tt(e,t,n){let o={...e,inputs:e.inputs.map((r,a)=>{let i=t[a],c=n(xt(i.lockingBytecode));if(!c)throw new Error(`no key for input ${a} \u2014 cannot sign`);let d=le({inputIndex:a,sourceOutputs:t,transaction:e},{coveredBytecode:i.lockingBytecode,signingSerializationType:Uint8Array.of(Ot)}),p=Lt(d),l=de.signMessageHashSchnorr(c.privateKey,p);if(typeof l=="string")throw new Error(`signing failed on input ${a}: ${l}`);let u=Uint8Array.from([...l,Ot]);return{...r,unlockingBytecode:Uint8Array.from([u.length,...u,c.publicKey.length,...c.publicKey])}})},s=he(o);return{transaction:o,hex:xt(s),txid:bt(s)}}function bt(e){return xt(Lt(e).slice().reverse())}function St(e){let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return bt(t)}var ue="e1cdbba719436f49acb4bf245b44df875c6d6b405eda46a9d3e736d138102f3b",Ht="BNS1";var pe=/^[a-z0-9-]{1,32}$/,fe=/^[a-z0-9-]{1,16}$/,ge=/^[a-z0-9-]{1,32}(\.[a-z0-9-]{1,16})?$/;function ye(e){return e.includes(".")?e:e+".bch"}var we=["wss://chipnet.bch.ninja:50004","wss://chipnet.imaginary.cash:50004","wss://cbch.loping.net:62002"],me=e=>{let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t},xe=e=>[...new TextEncoder().encode(e)].map(t=>t.toString(16).padStart(2,"0")).join("");function be(e){let n=String(e).toLowerCase().trim().replace(/^[a-z]+:\/\//,"").split("/")[0].replace(/\.$/,"").split(".").filter(Boolean);if(n.length===0)throw new Error(`invalid name "${e}"`);let o,s;if(n.length===1?(o=n[0],s="bch"):(s=n[n.length-1],o=n[n.length-2]),!pe.test(o)||!fe.test(s))throw new Error(`invalid name "${e}" (allowed: <label>.<tld>, [a-z0-9-])`);return`${o}.${s}`}var kt=class{constructor(t,n){this.url=t,this.WS=n||globalThis.WebSocket,this.id=0,this.pending=new Map,this.buf=""}connect(){return new Promise((t,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"]),t(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(t){let n=this.pending.get(t.id);n&&(this.pending.delete(t.id),t.error?n.reject(new Error(JSON.stringify(t.error))):n.resolve(t.result))}_onData(t){this.buf+=t;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(t,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:t,params:n})+`
|
|
`),setTimeout(()=>{this.pending.has(o)&&(this.pending.delete(o),r(new Error(`electrum timeout: ${t}`)))},2e4)})}close(){try{this.ws.close()}catch{}}};async function Se(e,t){let n;for(let o of e)try{return await new kt(o,t).connect()}catch(s){n=s}throw n??new Error("no electrum server reachable")}function ke(e){if(!e||!e.startsWith("6a"))return null;let t=me(e),n=1,o=[];for(;n<t.length;){let r=t[n++],a;if(r<=75)a=r;else if(r===76)a=t[n++];else if(r===77)a=t[n]|t[n+1]<<8,n+=2;else return null;o.push(t.slice(n,n+a)),n+=a}let s=new TextDecoder().decode(o[0]??new Uint8Array);if(!s.startsWith(Ht))return null;try{let r=JSON.parse(s.slice(Ht.length));if((r.o==="REG"||r.o==="UPD")&&typeof r.n=="string")return r}catch{}return null}function ve(e,t){let n=e.height>0?e.height:1/0,o=t.height>0?t.height:1/0;return n-o||e.tx_hash.localeCompare(t.tx_hash)}function Te(e,t){let n=new Set(e.map(i=>i.tx_hash)),o=new Map(e.map(i=>{let c=new Set;for(let d of t.get(i.tx_hash)?.vin??[])d.txid&&d.txid!==i.tx_hash&&n.has(d.txid)&&c.add(d.txid);return[i.tx_hash,c]})),s=new Set,r=[],a=[...e];for(;a.length;){let i=a.findIndex(d=>[...o.get(d.tx_hash)].every(p=>s.has(p)));i<0&&(i=0);let[c]=a.splice(i,1);s.add(c.tx_hash),r.push(c)}return r}async function J({electrum:e=we,WebSocket:t,client:n}={}){let o=n??await Se(e,t);try{let s=await o.call("blockchain.scripthash.get_history",[ue]);s.sort(ve);let r=new Map;for(let{tx_hash:i}of s)try{r.set(i,await o.call("blockchain.transaction.get",[i,!0]))}catch{}let a=new Map;for(let{tx_hash:i,height:c}of Te(s.filter(d=>r.has(d.tx_hash)),r)){let p=r.get(i).vout||[],l=null;for(let h of p)l=ke(h.scriptPubKey?.hex)??l;if(!l)continue;let u=l.n;if(!ge.test(u))continue;let g=ye(u);if(l.o==="REG"){if(a.has(g))continue;let h=xe(u),f=p.find(y=>y.tokenData?.nft&&y.tokenData.nft.commitment===h);if(!f)continue;a.set(g,{name:g,category:f.tokenData.category,records:l.r??{},txid:i,height:c,updatedTxid:null})}else if(l.o==="UPD"){let h=a.get(g);if(!h||!p.some(y=>y.tokenData&&y.tokenData.category===h.category))continue;h.records=l.r??{},h.updatedTxid=i}}return a}finally{n||o.close()}}async function Ee(e,t={}){let n=be(e);return(await J(t)).get(n)??null}var x={network:"chipnet",addressPrefix:"bchtest",electrum:pt,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 Ae(e){let t=n=>(Number(n)/1e8).toFixed(8).replace(/0+$/,"").replace(/\.$/,"");return[{label:"Service fee",sats:e.serviceFeeSats,bch:t(e.serviceFeeSats)},{label:"Network fee",sats:e.chainFeeSats,bch:t(e.chainFeeSats)},{label:"Discovery beacon",sats:e.beaconDustSats,bch:t(e.beaconDustSats)},{label:"Held in the certificate",sats:e.certificateDustSats,bch:t(e.certificateDustSats),note:"stays yours \u2014 it lives in the certificate you now own"},{label:"Total",sats:e.netCostSats,bch:t(e.netCostSats),total:!0}]}async function Be({electrum:e=x.electrum,WebSocket:t}={}){return $t(e,t)}async function Tt(e,t){let n=O(t),o=k(n),r=(await J({client:e})).get(o)??null;return{name:o,rawName:n,available:!r,entry:r,recordBudgetBytes:wt(n)}}async function Et(e,{name:t,ownerAddress:n,records:o={},utxos:s,serviceFee:r=x.serviceFee,feeRate:a=x.feeRate}){let i=s??await _(e,[n]);return mt({name:t,ownerAddress:n,ownerTokenAddress:F(n),records:o,utxos:i,feeAddress:r.sats>0n?r.address:null,feeSats:r.sats,beaconAddress:x.beacon,feeRate:a,addressToLockingBytecode:S})}async function _e(e,{wallet:t,name:n,records:o={},serviceFee:s=x.serviceFee,feeRate:r=x.feeRate,onProgress:a=()=>{}}){a("checking-availability");let i=await Tt(e,n);if(!i.available)throw new Error(`"${i.name}" is already registered`);a("loading-coins");let c=await _(e,t.watchedAddresses);if(c.filter(h=>!h.token).reduce((h,f)=>h+f.satoshis,0n)===0n)throw new Error("wallet is empty \u2014 fund the address first");let p=null;if(!Z(c)){a("preparing-wallet");let h=at({ownerAddress:t.address,utxos:c,feeRate:r,addressToLockingBytecode:S}),f=tt(h.transaction,h.sourceOutputs,y=>t.keyFor(y));p=await q(e,f.hex),a("prepared",{txid:p}),c=[{txid:p,vout:0,satoshis:f.transaction.outputs[0].valueSatoshis,address:t.address,lockingBytecode:S(t.address)},...f.transaction.outputs.length>1?[{txid:p,vout:1,satoshis:f.transaction.outputs[1].valueSatoshis,address:t.address,lockingBytecode:S(t.address)}]:[]]}a("building");let l=await Et(e,{name:n,ownerAddress:t.address,records:o,utxos:c,serviceFee:s,feeRate:r});a("signing");let u=tt(l.transaction,l.sourceOutputs,h=>t.keyFor(h));a("broadcasting");let g=await q(e,u.hex);return a("registered",{txid:g,category:l.category}),{name:l.name,displayName:l.displayName,category:l.category,txid:g,prepTxid:p,ownerAddress:t.address,certificateAddress:F(t.address),costs:l.costs,hex:u.hex}}async function vt(e,t,n,o){let s=n.signedTransactionHash??St(n.signedTransaction);if(t.walletBroadcasts)return s;o("broadcasting");try{return await q(e,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 Ne(e,{session:t,name:n,ownerAddress:o,records:s={},serviceFee:r=x.serviceFee,feeRate:a=x.feeRate,onProgress:i=()=>{}}){i("checking-availability");let c=await Tt(e,n);if(!c.available)throw new Error(`"${c.name}" is already registered`);i("reading-wallet");let d=await t.getAddresses();if(!d.length)throw new Error("the wallet exposed no addresses");let p=o??d[0];i("loading-coins");let l=await _(e,d);if(!l.some(f=>!f.token))throw new Error("the connected wallet has no spendable coins");if(!Z(l)){i("preparing-wallet");let f=at({ownerAddress:p,utxos:l,feeRate:a,addressToLockingBytecode:S}),y=await t.signTransaction(f,{userPrompt:"One-time setup so your wallet can create a name certificate"}),w=await vt(e,t,y,i);i("prepared",{txid:w}),l=[{txid:w,vout:0,satoshis:f.transaction.outputs[0].valueSatoshis,address:p,lockingBytecode:S(p)},...l.filter(b=>!b.token&&!f.inputUtxos.some(m=>m.txid===b.txid&&m.vout===b.vout))]}i("building");let u=await Et(e,{name:n,ownerAddress:p,records:s,utxos:l,serviceFee:r,feeRate:a});i("awaiting-approval");let g=await t.signTransaction(u,{userPrompt:`Register ${u.displayName} \u2014 the certificate is minted to your own address`}),h=await vt(e,t,g,i);return i("registered",{txid:h,category:u.category}),{name:u.name,displayName:u.displayName,category:u.category,txid:h,ownerAddress:p,certificateAddress:F(p),costs:u.costs}}async function Ie(e,{addresses:t,category:n,rawName:o}){let s=await _(e,t),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 $e(e,{wallet:t,name:n,records:o,feeRate:s=x.feeRate,onProgress:r=()=>{}}){r("resolving");let a=O(n),c=(await J({client:e})).get(k(a));if(!c)throw new Error(`"${k(a)}" is not registered`);r("loading-coins");let d=await _(e,t.watchedAddresses),p=d.find(h=>h.token?.category===c.category&&h.token?.nft);if(!p)throw new Error(`this wallet does not hold the certificate for "${k(a)}" \u2014 only the owner can change its records`);r("building");let l=it({name:n,ownerAddress:t.address,ownerTokenAddress:F(t.address),records:o,certificateUtxo:p,utxos:d.filter(h=>!h.token),beaconAddress:x.beacon,feeRate:s,addressToLockingBytecode:S});r("signing");let u=tt(l.transaction,l.sourceOutputs,h=>t.keyFor(h));r("broadcasting");let g=await q(e,u.hex);return r("updated",{txid:g}),{name:l.name,displayName:l.displayName,category:l.category,txid:g,records:o,costs:l.costs}}async function Re(e,{session:t,name:n,records:o,feeRate:s=x.feeRate,onProgress:r=()=>{}}){r("resolving");let a=O(n),c=(await J({client:e})).get(k(a));if(!c)throw new Error(`"${k(a)}" is not registered`);r("reading-wallet");let d=await t.getAddresses(),p=await _(e,d),l=p.find(f=>f.token?.category===c.category&&f.token?.nft);if(!l)throw new Error(`this wallet does not hold the certificate for "${k(a)}" \u2014 only the owner can change its records`);r("building");let u=it({name:n,ownerAddress:l.address,ownerTokenAddress:F(l.address),records:o,certificateUtxo:l,utxos:p.filter(f=>!f.token),beaconAddress:x.beacon,feeRate:s,addressToLockingBytecode:S});r("awaiting-approval");let g=await t.signTransaction(u,{userPrompt:`Point ${u.displayName} somewhere new`}),h=await vt(e,t,g,r);return r("updated",{txid:h}),{name:u.name,displayName:u.displayName,category:u.category,txid:h,records:o,costs:u.costs}}async function Ue(e,t){let n=await _(e,t),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:Z(n)}}import{hash160 as De,lockingBytecodeToCashAddress as Ce,binToHex as dn}from"@bitauth/libauth";function Wt(e){return Uint8Array.from([118,169,20,...De(e),136,172])}function Fe(e,t="bchtest",n=!1){let o=Ce({bytecode:Wt(e),prefix:t,tokenSupport:n});if(typeof o=="string")throw new Error(`address encoding failed: ${o}`);return o.address}function Oe(e){return e?{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 kn,hexToBin as vn}from"@bitauth/libauth";import{stringify as En}from"@bitauth/libauth";export{dt as BuiltInWallet,X as CHIPNET_PREFIX,R as InsufficientFundsError,st as NoGenesisInputError,x as REGISTRAR,S as addressToLockingBytecode,kn as binToHex,q as broadcast,J as buildIndex,at as buildPrepTx,mt as buildRegistrationTx,it as buildUpdateTx,Tt as checkAvailability,Be as connect,Oe as describeGenesisRequirement,k as displayName,Ie as findCertificateUtxo,Ue as getBalance,Rt as getUtxos,_ as getUtxosForAddresses,Z as hasGenesisInput,vn as hexToBin,O as normalizeName,wt as payloadBudget,Ae as priceSummary,Fe as pubkeyToAddress,Wt as pubkeyToLockingBytecode,Et as quoteRegistration,_e as registerWithBuiltInWallet,Ne as registerWithExternalWallet,Ee as resolveName,$e as setRecordsWithBuiltInWallet,Re as setRecordsWithExternalWallet,tt as signTransaction,En as stringify,F as toTokenAddress,Ft as toWcTransactionObject,bt as txidOf,St as txidOfHex};
|