memoscan
← all memos

Memo 0x34d3a364…e70aaf on Ethereum

0x68575b07…67c7·#25,913,385·view on Etherscan
async function cbAct(){ if(!cbDao||cbAmt<=0n)return; const b=document.getElementById("cbGo");if(!b||b.disabled)return; const label=b.textContent; try{ if(!account)return connect(); b.disabled=true;b.textContent=cbBuy?"Backing…":"Burning…"; const req=cbBuy ?{from:account,to:COFF,value:"0x"+cbCost.toString(16), data:"0x"+SEL_BUY+encAddr(cbDao)+encUint(cbAmt)} :{from:account,to:cbDao,value:"0x0", data:"0x"+SEL_RAGEQUIT+encUint(96)+encUint(0)+encUint(cbAmt)+encUint(1)+encAddr(ZERO)}; if(!cbBuy&&cbShares&&cbTok){ let now2=null; try{ const [b2,r2]=await Promise.all([ rpc("eth_getBalance",[cbDao,L]), mc3Deep([{to:cbShares,data:"0x"+SEL_TSUP},{to:cbTok,data:"0x"+SEL_TSUP}])]); const e2=BigInt(b2||"0x0"),s2=BigInt(r2[0]||"0x0")+BigInt(r2[1]||"0x0"); if(s2>0n)now2=e2*cbAmt/s2; }catch{} if(now2!==null&&now2<cbDue){ const lost=cbDue-now2; if(now2<=0n)throw Error("The treasury has been drawn down since this was quoted \u2014 burning now would pay nothing."); if(lost*100n>cbDue)throw Error(`This burn now pays ${trimAmt(now2,18)} ETH, not ${trimAmt(cbDue,18)} ` +`\u2014 the treasury was drawn down since it was quoted. Check the line again before burning.`); } } await rpc(C,[req,L]); await waitTx(await sendTx([req]));sGot(); if(cbBuy&&cbTok){ const kt=TOKENS.find(x=>x.addr&&x.addr.toLowerCase()===cbTok.toLowerCase()); if(kt){kt.cause=1;if(kt.cust)keepToken(kt)} } stat.textContent=cbBuy?"Backed · burn it back any time for what is left." :"Burned · the ether is in your wallet."; refreshBalance();cbSync(); }catch(e){err(e);b.disabled=false;b.textContent=label} } const LN_MIME={"image/png":0,"image/webp":1,"image/svg+xml":2,"image/gif":3,"image/jpeg":4}; const MIME_NAME=["image/png","image/webp","image/svg+xml","image/gif","image/jpeg","image/avif"]; const LN_MAX_ART=24575; const LN_BUDGET=8192; const lnCause=()=>lnKind.value==="cause"; const lnCap=()=>lnCause()?CAU_MAX_ART:LN_MAX_ART; const lnBud=()=>lnCause()?CAU_MAX_ART:LN_BUDGET; const LN_MAX_ALLOC=2000; const LN_MIN_POOLED=2000000000000n; const LN_MIN_MCAP=1000000000000n; const lnHex=b=>{let h="";for(const x of b)h+=x.toString(16).padStart(2,"0");return h}; const lnTail=b=>{const h=lnHex(b);return encUint(b.length)+h.padEnd(Math.ceil(h.length/64)*64,"0")}; const encLaunch=(n,y,u,sup,alloc,mcap,own,art,mime)=>{ const T=new TextEncoder(); const dyn=[T.encode(n),T.encode(y),T.encode(u)]; const statics=[encUint(sup),encUint(alloc),encUint(mcap),encAddr(own)]; if(art){dyn.push(art);statics.push(encUint(mime))} const words=statics.length+(art?4:3); let off=words*32,head="",tail=""; const heads=[]; for(const b of dyn){heads.push(encUint(off));off+=lnTail(b).length/2;tail+=lnTail(b)} head=heads.slice(0,3).join("")+statics.slice(0,4).join("") +(art?heads[3]+statics[4]:""); return "0x"+(art?SEL_LAUNCHART:SEL_LAUNCH)+head+tail; }; async function lnOwner(){ const rv=rc.value.trim(); if(!rv)return account; if(/^0x[0-9a-fA-F]{40}$/.test(rv)){ if(/^0x0{40}$/i.test(rv))throw Error("That cannot be the zero address."); return rv; } if(!/\.(g?wei|eth)$/i.test(rv))throw Error("That must be an address, or a .eth / .wei name."); rcvEl.textContent="resolving\u2026"; let r="";try{r=await nameFwd(rv.toLowerCase())}catch{r=""} if(!r||/^0x0{40}$/i.test(r)){rcvEl.textContent="";throw Error("That name is not registered.")} if(!isAddr(r))throw Error("That name resolved to something that is not an address."); rcvEl.textContent=r; return r; } const CSUM="0x00000000004473e1f31C8266612e7FD5504e6f2a"; const CMSUM="0x0000000000330B8df9E3bc5E553074DA58eE9138"; const CIMPL="643a45b599d81be3f3a68f37eb3de55ff10673c1"; const CRND="0x000000000011C799980827F52d3137b4abD6E654"; const COFF="0x000000A4Ad929C9E108aD2B1D2fBeDe0C2Ae57e1"; const CTAP="0x0000000060cdD33cbE020fAE696E70E7507bF56D"; const CLOOT="0x00000000000000000000000000000000000003ef"; const SEL_DAICO="4e1e3b11",SEL_SETALLOW="da46098c",SEL_CONFIGURE="fca53be5"; const CUNITS=10000000n,CONE=10n**18n,CMAX=(1n<<256n)-1n; const CAU_MAX_ART=4096; const CAU_MAX_DAYS=3650; const cPad=h=>h.padEnd(Math.ceil(h.length/64)*64,"0"); const cTail=h=>{h=strip0x(h);return encUint(h.length/2)+cPad(h)}; const cCall=(to,data)=>encAddr(to)+encUint(0)+encUint(96)+cTail(data); const cCalls=cs=>{let off=cs.length*32,head="",tail=""; for(const c of cs){head+=encUint(off);off+=c.length/2;tail+=c} return encUint(cs.length)+head+tail}; const CLIMPL="6f1f2af76a3add953277e9f369242697c87bc6a5"; const cProxy=impl=>strip0x(keccak(hexToBytes( "602d5f8160095f39f35f5f365f5f37365f73"+impl+"5af43d5f5f3e6029573d5ffd5b3d5ff3"))); const cLoot=dao=>{const d=strip0x(dao).toLowerCase(); return "0x"+keccak(hexToBytes("ff"+d+d+"0".repeat(24)+cProxy(CLIMPL))).slice(26)}; const cPredict=(owner,salt)=>{ const inner=encUint(96)+encUint(160)+strip0x(salt) +encUint(1)+encAddr(owner)+encUint(1)+encUint(CONE); const ss=strip0x(keccak(hexToBytes(inner))); const ih=cProxy(CIMPL); return "0x"+keccak(hexToBytes("ff"+strip0x(CMSUM)+ss+ih)).slice(26); }; const encCause=(name,sym,uri,salt,owner,ben,dao,goal,rate,price,deadline,cap)=>{ const T=new TextEncoder(),Z=encUint(0); const dyn=[lnTail(T.encode(name)),lnTail(T.encode(sym)),lnTail(T.encode(uri)), encUint(1)+encAddr(owner),encUint(1)+encUint(CONE),encUint(0), cCalls([ cCall(dao,SEL_SETALLOW+encAddr(COFF)+encAddr(CLOOT)+encUint(CMAX)), cCall(COFF,SEL_CONFIGURE+encAddr(CLOOT)+encAddr(ZERO)+encUint(price) +encUint(deadline)+encUint(cap))])]; const cfg=encUint(CONE)+encUint(604800)+encUint(259200)+encUint(CONE)+Z.repeat(17); const tap=encAddr(CTAP)+Z+encUint(goal)+encAddr(ben)+encUint(rate); let off=52*32,heads=[],tail=""; for(const d of dyn){heads.push(encUint(off));off+=d.length/2;tail+=d} return "0x"+SEL_DAICO +heads[0]+heads[1]+heads[2] +encUint(1000)+encUint(1)+encAddr(CRND)+strip0x(salt) +heads[3]+heads[4]+heads[5] +cfg+Z.repeat(7)+tap+Z.repeat(8)+heads[6]+tail; }; const cauURI=(name,sym)=>{ let img=""; if(lnArtBytes){let s="";for(const x of lnArtBytes)s+=String.fromCharCode(x); img="data:"+MIME_NAME[lnArtMime]+";base64,"+btoa(s)} return "data:application/json,"+encodeURIComponent( JSON.stringify({name,symbol:sym,image:img,launchType:"cause"})); }; function cauTerms(){ const g=String(lnGoal.value||"").replace(/[, ]/g,"").trim(); const d=String(lnDays.value||"").replace(/[, ]/g,"").trim(); if(!/^\d+(\.\d+)?$/.test(g)||!/^\d+(\.\d+)?$/.test(d))return null; let goal;try{goal=parseUnits(g,18)}catch{return null} const secs=BigInt(Math.round(Number(d)*86400)); if(goal<=0n||secs<3600n||Number(d)>CAU_MAX_DAYS)return null; const price=goal/CUNITS; if(price<=0n)return null; const vest=BigInt(lnVest.value); if(vest<=secs)return{err:`Backing runs for ${trimNum(Number(secs)/86400)} days but the ether is ` +`fully released in ${trimNum(Number(vest)/86400)} days — the treasury would empty before backing closes. ` +`Shorten the raise, or release over longer.`}; const rate=goal/vest||1n; return{goal,secs,price,rate,vest,cap:(CUNITS-1n)*CONE}; } const lnKindQuote=()=>lnKind.value==="cause"?cauQuote():lnQuote(); function cauQuote(){ lnGo.textContent=account?"Launch":"Connect Wallet"; const t=cauTerms(); if(!t){lnNote.textContent="";return} if(t.err){lnNote.textContent=t.err;lnNote.style.color="var(--w)";return} const per=(+formatUnits(t.price,18)); const tenthDays=t.rate>0n ?Number((t.goal/10n)/t.rate)/86400 :0; lnNote.textContent=`1 ETH backs ${trimNum(1/per)} units` +` · ${trimNum(+formatUnits(t.rate*86400n,18))} ETH a day at full funding, over ` +lnVest.options[lnVest.selectedIndex].textContent +(tenthDays>0?` · at a tenth of the goal it empties in ${trimNum(Math.round(tenthDays*10)/10)} days`:"") +` · backers burn back for the rest` +(lnArtBytes?` · logo ${(lnArtBytes.length/1024).toFixed(1)} KB` +(lnArtBytes.length>CAU_MAX_ART?` — over the 4 KB a cause allows, pick something simpler`:""):""); lnNote.style.color=lnArtBytes&&lnArtBytes.length>CAU_MAX_ART?"var(--w)":""; } function cauKind(){ const on=lnKind.value==="cause"; for(const el of [lnMktL,lnSupplyL,lnMcapL,lnAllocL])el.classList.toggle("hide",on); for(const el of [lnGoalL,lnDaysL,lnVestL])el.classList.toggle("hide",!on); lnTitle.textContent=on?"Launch a cause":"Launch a coin"; lnSub.textContent=on?"backers burn back for whatever you have not drawn" :"0.4% of each trade goes to the creator"; lnName.placeholder=on?"Feed Ducks":"Zero Cat"; lnSym.placeholder=on?"DUCK":"ZCAT"; rc.placeholder=on?"Beneficiary — defaults to you":"Creator — defaults to you"; rc.title=on?"Who the ether streams to. Leave blank and it is you. Worth setting to a multisig if the cause has one." :"Who the token belongs to: receives the creator allocation, collects the creator share of trading fees for the life of the token, and may edit its name, symbol and logo. Leave blank and it is you. Worth setting to a multisig if the project has one."; stat.textContent=""; on?cauQuote():lnQuote(); } async function cauGo(){ const name=lnName.value.trim(),sym=lnSym.value.trim().toUpperCase(); if(!name||!sym)throw Error("A name and a symbol, please."); const utf8=v=>new TextEncoder().encode(v).length; if(utf8(name)>64)throw Error(`That name is ${utf8(name)} bytes and the limit is 64.`); if(utf8(sym)>16)throw Error(`That symbol is ${utf8(sym)} bytes and the limit is 16.`); if(lnArtBytes&&lnArtBytes.length>CAU_MAX_ART) throw Error(`A cause's logo is stored in the DAO itself, so it is capped at 4 KB. ` +`That one is ${(lnArtBytes.length/1024).toFixed(1)} KB — pick something simpler.`); const t=cauTerms(); if(!t)throw Error("A goal in ether, and how long it runs for — at least an hour, at most ten years."); if(t.err)throw Error(t.err); const ben=await lnOwner(); const owner=account; const salt=wnSecret(); const dao=cPredict(owner,salt); const deadline=BigInt(Math.floor(Date.now()/1000))+t.secs; stat.textContent="Launching…"; const req={from:account,to:CSUM, value:"0x"+t.price.toString(16), data:encCause(name,sym,cauURI(name,sym),salt,owner,ben,dao,t.goal,t.rate,t.price,deadline,t.cap)}; await rpc(C,[req,L]); await waitTx(await sendTx([req]));sGot(); lnArtBytes=null;lnArtNote.textContent="none";lnArtNote.classList.remove("set"); lnPrev.classList.remove("set");lnArt.value=""; let loot=cLoot(dao); try{ const said=cbWord(await rpc(C,[{to:dao,data:"0x"+SEL_LOOTOF},L])); if(isAddr(said||"")&&said.toLowerCase()!==loot.toLowerCase())loot=said; }catch{} try{const ix=await addCustomToken(loot);TOKENS[ix].cause=1;rebuild()}catch{} stat.innerHTML=`${safeSym(sym)} is raising at ` +`<a href="${chainCfg().explorer}/token/${loot}" target="_blank" rel="noreferrer">${short(loot)}</a>` +` · ${trimNum(+formatUnits(t.goal,18))} ETH over ${trimNum(Number(t.secs)/86400)} days` +` · backers can burn back for whatever you have not drawn`; } let lnMode=false,lnArtBytes=null,lnArtMime=0; function lnSet(on){ if(on&&PLAUNCH===ZERO)return; if(on)cbDisarm(); ++seq;quoting=false; lnMode=on; if(on&&lqMode)lqSet(false); if(on&&wnMode)wnSet(false); if(on&&pvMode)pvSet(false); ln.setAttribute("aria-pressed",on?"true":"false"); ln.classList.toggle("on",on); for(const el of [rate,flip,swap])if(el)el.classList.toggle("hide",on); for(const el of [document.querySelector(".panel"),rcvPanel])if(el)el.classList.toggle("hide",on); document.querySelector(".card").classList.toggle("lnon",on); for(const el of [lnPanel,lnNote,lnGo])el.classList.toggle("hide",!on); rc.value="";rc.classList.remove("bad");rcvEl.textContent=""; rc.title=on?"Who the token belongs to: receives the creator allocation, collects the creator share of trading fees for the life of the token, and may edit its name, symbol and logo. Leave blank and it is you. Worth setting to a multisig if the project has one.":""; rc.placeholder=on?"Creator — defaults to you" :"Recipient (optional) — 0x or .eth/.(g)wei"; stat.textContent="";lnNote.textContent=""; if(on)cauKind(); fcSync(); } function lnQuote(){ lnGo.textContent=account?"Launch":"Connect Wallet"; const ns=v=>v.replace(/[, ]/g,""); const sup=ns(lnSupply.value),mc=+ns(lnMcap.value),al=+ns(lnAlloc.value); lnNote.textContent=""; if(!(Number(sup)>0)||!(mc>0))return; const alBps=al>0?Math.round(al*100):0; const pooled=Number(sup)*(1-alBps/10000); if(!(pooled>0))return; const sym=lnSym.value.trim().toUpperCase(); const poolFrac=99/(mc+.99); const firstEth=poolFrac*pooled/Number(sup); const firstTokens=poolFrac*pooled/100; lnNote.textContent=`1 ETH buys ${firstEth>=10?Math.round(firstEth):firstEth.toFixed(firstEth<1?2:1)}% of all ${sym||"tokens"}` +` \u00b7 opens at 1 ETH \u2248 ${trimNum(firstTokens)} ${sym||"tokens"}` +(al>0&&alBps===0?` \u00b7 that keep is below one basis point \u2014 the smallest is 0.01%`:"") +(alBps>0?` \u00b7 creator keeps ${trimNum(Number(sup)*alBps/10000)} (${alBps/100}%)` +` \u00b7 fully diluted ${trimNum(mc*Number(sup)/pooled)} ETH`:"") +(alBps>=1000?` \u00b7 that much can sell for most of the ether early buyers put in`:"") +(lnArtBytes?` \u00b7 logo ${(lnArtBytes.length/1024).toFixed(1)} KB`:""); lnNote.style.color=poolFrac>=10?"var(--w)":""; } const trimNum=n=>n>=1000?Math.round(n).toLocaleString("en-US") :n>=1?n.toLocaleString("en-US",{maximumFractionDigits:4}):String(Number(n.toPrecision(4))); const blobText=b=>b.text?b.text():new Promise((res,rej)=>{ const r=new FileReader();r.onload=()=>res(String(r.result));r.onerror=()=>rej(r.error);r.readAsText(b)}); const blobBytes=async b=>b.arrayBuffer?new Uint8Array(await b.arrayBuffer()):await new Promise((res,rej)=>{ const r=new FileReader();r.onload=()=>res(new Uint8Array(r.result));r.onerror=()=>rej(r.error);r.readAsArrayBuffer(b)}); async function lnPrepare(file){ if(/svg/.test(file.type)){ const b=new TextEncoder().encode((await blobText(file)) .replace(/<\?xml[^>]*\?>/g,"").replace(/<!--[\s\S]*?-->/g,"").replace(/<!DOCTYPE[^>]*>/gi,"") .replace(/<script[\s\S]*?<\/script\s*>/gi,"").replace(/<script[\s\S]*$/gi,"") .replace(/<foreignObject[\s\S]*?<\/foreignObject\s*>/gi,"").replace(/<foreignObject[\s\S]*$/gi,"") .replace(/\son[a-z-]+\s*=\s*("[^"]*"|'[^']*'|[^\s>]*)/gi,"") .replace(/\s(?:[a-z]+:)?href\s*=\s*("\s*javascript:[^"]*"|'\s*javascript:[^']*'|javascript:[^\s>]*)/gi,"") .replace(/>\s+</g,"><").trim()); if(b.length>lnCap())throw Error(`That SVG is ${(b.length/1024).toFixed(1)} KB and the on-chain limit is ${Math.round(lnCap()/1024)} KB.`); return{bytes:b,mime:LN_MIME["image/svg+xml"]}; } const bmp=await createImageBitmap(file); let smallest=null; for(const side of [512,384,256,192,128,96,64]){ for(const [type,q] of [["image/webp",1],["image/png",undefined],["image/webp",0.85],["image/webp",0.6]]){ const sc=Math.min(1,side/Math.max(bmp.width,bmp.height)); const cv=document.createElement("canvas"); cv.width=Math.max(1,Math.round(bmp.width*sc));cv.height=Math.max(1,Math.round(bmp.height*sc)); const cx=cv.getContext("2d");cx.imageSmoothingQuality="high"; cx.drawImage(bmp,0,0,cv.width,cv.height); const blob=await new Promise(r=>cv.toBlob(r,type,q)); if(!blob||blob.type!==type)continue; const bytes=await blobBytes(blob); if(!smallest||bytes.length<smallest.bytes.length)smallest={bytes,mime:LN_MIME[type]}; if(bytes.length<=lnBud())return{bytes,mime:LN_MIME[type]}; } } if(smallest&&smallest.bytes.length<=lnCap())return smallest; throw Error("That image will not compress small enough. Flat colours and simple shapes do far better than photographs."); } lnPick.onclick=()=>lnArt.click(); lnArt.onchange=async()=>{ const f=lnArt.files&&lnArt.files[0]; if(!f)return; lnArtNote.textContent="reading…"; try{ const r=await lnPrepare(f); lnArtBytes=r.bytes;lnArtMime=r.mime; lnArtNote.textContent=`${(r.bytes.length/1024).toFixed(1)} KB`; lnArtNote.classList.add("set"); let b64="";for(const x of r.bytes)b64+=String.fromCharCode(x); lnPrev.src="data:"+MIME_NAME[r.mime]+";base64,"+btoa(b64); lnPrev.classList.add("set"); }catch(e){ lnArtBytes=null;lnArtNote.classList.remove("set");lnPrev.classList.remove("set"); lnArtNote.textContent="none";stat.textContent=e.message||String(e); } lnKindQuote(); }; const lnGroup=el=>{ if(!/^[\d, ]*(\.[\d ]*)?$/.test(el.value))return; const dot=el.value.indexOf("."); const intRaw=(dot<0?el.value:el.value.slice(0,dot)).replace(/\D/g,""); const frac=dot<0?"":"."+el.value.slice(dot+1).replace(/\D/g,""); const digitsBefore=el.value.slice(0,el.selectionStart??el.value.length).replace(/\D/g,"").length; const out=intRaw.replace(/\B(?=(\d{3})+(?!\d))/g,",")+frac; if(out===el.value)return; el.value=out; let seen=0,pos=digitsBefore?out.length:0; if(digitsBefore)for(let i=0;i<out.length;i++){ if(/\d/.test(out[i])&&++seen===digitsBefore){pos=i+1;break} } try{el.setSelectionRange(pos,pos)}catch{} }; lnSupply.oninput=()=>{lnGroup(lnSupply);lnQuote()}; for(const el of [lnName,lnSym,lnMcap,lnAlloc])el.oninput=lnKindQuote; lnKind.onchange=cauKind; for(const el of [lnGoal,lnDays])el.oninput=cauQuote; lnVest.onchange=cauQuote; ln.onclick=()=>{sBlip();lnSet(!lnMode)}; let wnMode=false,wnQ=null,wnSeq=0,wnRollSeq=0,wnTimer=0,wnRound=0n,wnStampTry=0,wnRollAt=0; const wnOk=l=>/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(l)&&new TextEncoder().encode(l).length<256; const wnFree=()=>wnTld.value==="id"; const wnOffer=(t,id)=>{wnEnter.classList.remove("hide");wnEnter.textContent=t; wnEnter.dataset.id=id||"";wnEnter.classList.toggle("mut",!id&&!!account)}; const wnFull=l=>l+(wnFree()?".id.wei":".wei"); const wnSecret=()=>{const a=new Uint8Array(32);crypto.getRandomValues(a);let h="";for(const x of a)h+=x.toString(16).padStart(2,"0");return"0x"+h}; const wnHash=(l,o,sec)=>keccak(hexToBytes(pad32("60")+encAddr(o)+pad32(sec)+strTail(l))); const wnKey=()=>WN_KEY+":"+(account||"").toLowerCase(); const wnStamp=async()=>{const p=wnPend(); if(!p||p.at||!account)return; try{const r=await rpc(C,[{to:WNS,data:"0x"+SEL_WCOMMITS+pad32(wnHash(p.label,account,p.secret))},L]); const t=Number(dec1(r||"0x0",0)); if(t)wnSave({...p,at:t})}catch{}}; const wnPend=()=>{try{const p=JSON.parse(LS[wnKey()]||"null");return p&&p.label&&p.secret&&Number.isFinite(p.at)?p:null}catch{return null}}; const wnSave=p=>{try{if(p)LS[wnKey()]=JSON.stringify(p);else delete LS[wnKey()]}catch{}}; const wnId=n=>namehash(/\.wei$/.test(n)?n:n+".wei"); const wnCostCall=l=>[{to:WNS,data:"0x"+SEL_WFEE+encUint(new TextEncoder().encode(l).length)}, {to:WNS,data:"0x"+SEL_WPREM+pad32(wnId(l))}]; const wnCost=async l=>{const [f,pr]=await mc3(wnCostCall(l));return dec1(f,0)+dec1(pr,0)}; const wnSync=()=>{ if(!wnMode)return; const p=wnPend(); if(p&&!p.at){ if(!p.since){wnSave({...p,since:Math.floor(Date.now()/1000)});wnGo.disabled=true;wnGo.textContent="Confirming\u2026";return} if(Math.floor(Date.now()/1000)-p.since>600){wnGo.disabled=false;wnGo.textContent="Commit again";return} wnGo.disabled=true;wnGo.textContent="Confirming\u2026";return} if(p){ const left=WN_MIN-(Math.floor(Date.now()/1000)-p.at); wnGo.disabled=left>0; wnGo.textContent=left>0?`Ready in ${left}s`:`Register ${p.label}.wei`; if(Math.floor(Date.now()/1000)-p.at>=WN_MAX){wnGo.disabled=false;wnGo.textContent="Commit again"} return} wnGo.disabled=!wnQ; wnGo.textContent=wnQ?(wnQ.free?`Claim ${wnQ.label}.id.wei`:"Commit"):"Register"}; const wnTicketOffer=async(l,seq,round)=>{ try{const[t]=await mc3([{to:WROLL,data:"0x"+SEL_RTICKET+encUint(round)+pad32(wnId(l))}]); if(seq!==wnSeq)return; const inAlready=!!dec1(t,0); wnOffer(inAlready?`${l}.wei is already in`:`Enter ${l}.wei in the roll`,inAlready?"":pad32(wnId(l))); }catch{if(seq===wnSeq)wnOffer(`Could not read the roll for ${l}.wei`)}}; const wnQuote=async()=>{ const my=++wnSeq; wnQ=null; wnOffer(account?"Enter a name you own":"Connect to enter"); wnSub.textContent=wnFree()?"free":"from 0.0005 ETH a year"; const l=wnName.value.trim().toLowerCase(); const p=wnPend(); if(p){wnNote.textContent=p.at?`${p.label}.wei is held for you — register it within 24 hours or the commitment lapses.` :`Waiting for the commitment on ${p.label}.wei to confirm.`;wnSync();return} if(!l){wnNote.textContent="";wnSync();return} if(!wnOk(l)){wnNote.textContent="Letters, digits and hyphens only, and not starting or ending with a hyphen.";wnSync();return} wnNote.textContent="checking…"; wnSync(); try{ const free=wnFree(); const rc=!free&&account?[{to:WNS,data:"0x"+SEL_WOWNER+pad32(wnId(l))}, {to:WROLL,data:"0x"+SEL_RWEIGHT+pad32(wnId(l))}, {to:WROLL,data:"0x"+SEL_RSTATE}]:[]; const r=await mc3([{to:WNS,data:"0x"+SEL_WAVAIL+pad32("40")+pad32(free?IDWEI:"0")+strTail(l)}, ...(free?[]:wnCostCall(l)),...rc]); if(my!==wnSeq)return; const av=r[0],f=r[1],pr=r[2]; if(!account)wnOffer("Connect to enter"); else if(free)wnOffer("A free name cannot enter the roll"); else if(dec1(av,0)===1n)wnOffer(`${l}.wei is not registered yet`); else{const ow=retAddr(r[3]),mine=!!ow&&ow.toLowerCase()===account.toLowerCase(); const wt=r[4]&&dec1(r[4],0); if(r[5])wnRound=dec1(r[5],1); if(!mine||!wt)wnOffer(!mine?`${l}.wei is not yours`:`${l}.wei does not weigh`); else wnTicketOffer(l,my,wnRound)} if(dec1(av,0)!==1n){wnNote.textContent=`${wnFull(l)} is taken.`;wnSync();return} if(free){wnQ={label:l,free:true}; wnNote.textContent=`${l}.id.wei is free, in one transaction. A free name does not weigh in the roll.`;wnSync();return} const cost=dec1(f,0)+dec1(pr,0); wnQ={label:l,free:false,cost}; wnNote.textContent=`${l}.wei costs ${trimAmt(cost,18)} ETH for a year, in two transactions a minute apart.`; }catch(e){if(my===wnSeq)wnNote.textContent="Could not price that name."} wnSync()}; const wnRoll=async()=>{ if(!wnMode)return; const my=++wnRollSeq; wnRollAt=Date.now(); try{ const [st,rv]=await mc3([{to:WROLL,data:"0x"+SEL_RSTATE}, ...(account?[{to:WNS,data:"0x"+SEL_REV+encAddr(account)}]:[])]); const round=dec1(st,1),end=Number(dec1(st,2)),pot=dec1(st,3),tix=dec1(st,5),tw=dec1(st,6); if(my!==wnRollSeq)return; const days=Math.max(0,Math.ceil((end-Date.now()/1000)/86400)); let line=`A monthly draw among paid .wei names. ${(+formatUnits(pot,18)).toFixed(3)} ETH staked, ${tix} entered, ${days} day${days===1?"":"s"} left.`; wnRound=round; const typed=wnName.value.trim(); if(!typed)wnOffer(account?"Enter a name you own":"Connect to enter"); if(account&&!typed){ const nm=decodeString(rv||"0x").trim(); if(!nm)line+=" Any paid .wei name you own can enter, free — type one above."; else{ const id=wnId(nm); const [w,t]=await mc3([{to:WROLL,data:"0x"+SEL_RWEIGHT+pad32(id)}, {to:WROLL,data:"0x"+SEL_RTICKET+encUint(round)+pad32(id)}]); if(my!==wnRollSeq||wnName.value.trim()){wnRollEl.textContent=line;return} const weight=dec1(w,0); if(!weight){line+=` ${nm} does not weigh — only a paid .wei name does.`; wnOffer("Enter a name you own")} else if(dec1(t,0)){const odds=tw?Number(weight*10000n/tw)/100:0; line+=` ${nm} is in, at about ${odds<0.01?"<0.01":odds.toFixed(2)}%.`} else{line+=` ${nm} can enter, free.`;wnOffer(`Enter ${nm} in the roll`,pad32(id))}}} wnRollEl.textContent=line; }catch{wnRollEl.textContent="Could not read the roll."}}; const wnPaint=()=>{const on=wnMode; wn.setAttribute("aria-pressed",on?"true":"false"); wn.classList.toggle("on",on); for(const el of [rate,flip,swap])if(el)el.classList.toggle("hide",on); for(const el of [document.querySelector(".panel"),rcvPanel])if(el)el.classList.toggle("hide",on); for(const el of [wnPanel,wnGo,wnRollPanel])el.classList.toggle("hide",!on); for(const el of [rc,rcvEl])if(el)el.classList.toggle("hide",on);document.querySelector(".card").classList.toggle("wnon",on)}; function wnSet(on){ if(on&&CHAIN_ID!==1)return; if(on)cbDisarm(); ++seq;quoting=false; wnMode=on; if(on&&lqMode)lqSet(false); if(on&&lnMode)lnSet(false); if(on&&pvMode)pvSet(false); wnPaint(); stat.textContent=""; clearInterval(wnTimer); if(on){wnTimer=setInterval(()=>{ if(document.hidden)return; wnSync(); if(Date.now()-wnRollAt>30000)wnRoll(); const q=wnPend(); if(q&&!q.at&&Date.now()-wnStampTry>5000){wnStampTry=Date.now(); wnStamp().then(()=>{const r=wnPend();if(r&&r.at)wnQuote()})} },1000); wnStampTry=0; const pp=wnPend();if(pp&&!pp.at)wnStamp().then(wnQuote); wnQuote();wnRoll()} else{wnNote.textContent="";wnRollEl.textContent=""} fcSync();} const syncSnd=()=>{sdw.style.display=snd?"":"none";sdx.style.display=snd?"none":""; sd.style.opacity=snd?"":".5";sd.setAttribute("aria-pressed",snd?"true":"false"); sd.title=snd?"Sound on — click to mute":"Sound off