memoscan
← all memos

Memo 0xa01a2a37…ee7392 on Ethereum

0x68575b07…67c7·#25,913,257·view on Etherscan
t cpSettleWrap(n);pvAmt.value="";refreshBalance(); stat.textContent="Deposited. The relay is settling it into the pool.";sGot()}; const cpExit=async n=>{ const ch=+pvChain.value,to=pvTo.value.trim()||account,self=pvPath.value==="self",k=cpNoteOf(n); if(n.ex&&n.ex.job&&n.ex.js!=="failed")throw Error("This note is already exiting."); if(!isAddr(to))throw Error("The recipient must be a 0x address on "+CHAINS[ch].name+"."); if(!CP_L2[ch])throw Error("Unknown chain."); const c=await cpSync(true),li=c.leaves.indexOf(k.leaf); if(li<0)throw Error("This deposit has not settled into the pool yet."); if(c.spent.includes(k.nu))throw Error("This note is already spent."); if(c.leaves.length<20)pvNote.textContent="The pool holds only "+c.leaves.length+" notes, so an exit today is easy to link. Treat it as a test, not as privacy."; const impl=await cpPins(ch); const fee=self?0n:await cpFee(k.value); if(!self&&fee>=k.value)throw Error("This note is too small to relay — submit it yourself instead."); if(!self&&fee*10000n/k.value>300n)throw Error("The relay fee would be "+(Number(fee*10000n/k.value)/100).toFixed(1)+"% of this note — wait for cheaper gas, or submit it yourself."); const net=k.value-fee,wei=net*CP_SCALE; const code=await cfgRead("eth_getCode",[to,L]); const x={ch,to,dl:String((Math.floor(Date.now()/86400000)+3)*86400),nonce:big(sha2(cpU8("zswap-exit-nonce-v1:"+cpFp+":"+n.i))).toString(), fee:String(fee),wei:String(wei),self,fin:to}; if(strip0x(code||"")!==""){x.fin=cpRescue(n.i).addr;x.rk=1; if(ch===4663)pvNote.textContent="That recipient has contract code on Ethereum, so Robinhood will refund unused gas to an aliased address rather than to it."} if(ch===4663){const g=await cpRhGas(to);x.gl=String(g.gl);x.sc=String(g.sc);x.mf=String(g.mf); const over=g.sc+g.gl*g.mf;if(over>=wei)throw Error("The note would be eaten by Robinhood's gas budget.");x.l2v=String(wei-over)} const r=cpRecipe(x),esc=cpEscrow(impl,r); const onchain=retAddr(await cfgRead(C,[{to:CP_ROUTER,data:"0x"+SEL_CPESCROW+pad32("20")+cpEncRecipe(r)},L])); if(onchain!==esc)throw Error("Escrow address mismatch — refusing to build a proof."); x.esc=esc; const warn=await cpDry(x,r,esc); const{root,path}=cpTree(c.leaves,li); const dlu=String(Math.ceil((Math.floor(Date.now()/1000)+3600)/600)*600),cb=cpBinding(); const ctx=cpCtx("tacit-unwrap-intent-v1",cb,CP_ETH,pad32(strip0x(esc)),[[k.cx,k.cy,k.owner]],[k.value,fee,BigInt(dlu)]); const sig=cpSigma(k.value,k.blinding,ctx,cpNonce(k.blinding,ctx,"unwrap")); const op={chainBinding:cb,spendRoot:root,asset:CP_ETH,cx:k.cx,cy:k.cy,owner:k.owner,leafIndex:li,path,nk:k.secret,secret:k.secret, value:String(k.value),recipient:esc,fee:String(fee),deadline:dlu,sigR:sig.R,sigZ:sig.z}; n.ex=x;cpSaveNotes();cpPaint(); const r2=await relayFetch("/confidential/submit",{type:"unwrap",op,memos:[],mode:self?"prove":"settle"}); if(!r2.jobId)throw Error("The relay did not take the exit."); x.job=r2.jobId;x.js=r2.status||"pending";cpSaveNotes(); stat.textContent=(self?"Proving. The wallet will ask you to send the exit when the proof is ready.":"The relay is settling the exit. Activate it once it lands.")+(warn?" "+warn:""); if(self)await cpSelfSend(n)}; const cpSelfSend=async n=>{const x=n.ex; for(let t=0;t<180&&!(x.pv&&x.pr);t++){await new Promise(r=>setTimeout(r,5000));await cpRelayPoll(n); if(x.js==="failed")throw Error("The prover failed: "+(x.jerr||"unknown")); if(x.js==="settled")return cpPaint()} if(!(x.pv&&x.pr))throw Error("The proof is taking a while — it keeps proving; come back and press Send."); await checkWallet(); const req={from:account,to:CP_ROUTER,value:"0x0",data:cpExitData(x.pv,x.pr,cpRecipe(x)),gas:toHex(2000000)}; await rpc(C,[req,L]); x.ptx=await sendTx([req]);cpSaveNotes();cpPaint(); await settle(x.ptx);stat.textContent="Bridged. It lands on "+CHAINS[x.ch].name+" in a few minutes."}; const cpActivate=async n=>{const x=n.ex,r=cpRecipe(x),now=Math.floor(Date.now()/1000); if(now>Number(x.dl))throw Error("The exit expired — reclaim it instead."); if(!await cpEnsureBalance(x.esc,BigInt(x.wei)))throw Error("The escrow is not funded yet — the relay has not settled."); const req={from:account,to:CP_ROUTER,value:"0x0",data:cpActData(r),gas:toHex(CP_L2[x.ch].gas)}; await rpc(C,[req,L]); x.atx=await sendTx([req]);cpSaveNotes();cpPaint(); await settle(x.atx);stat.textContent="Bridged. It lands on "+CHAINS[x.ch].name+" in a few minutes."}; const cpReclaim=async n=>{const x=n.ex,r=cpRecipe(x); if(Math.floor(Date.now()/1000)<=Number(x.dl))throw Error("Reclaim opens after "+new Date(Number(x.dl)*1000).toLocaleString()+"."); const req={from:account,to:CP_ROUTER,value:"0x0",data:cpReclData(r),gas:toHex(400000)}; await rpc(C,[req,L]); x.rtx=await sendTx([req]);cpSaveNotes();cpPaint(); await settle(x.rtx);stat.textContent="Returned to "+x.fin+" on Ethereum."}; const cpRecover=async()=>{const c=await cpSync(true);let found=0; for(const[id,amt]of c.wraps){const wei=BigInt(amt);if(wei%CP_SCALE)continue;const v=wei/CP_SCALE; if(cpNotes.some(n=>BigInt(n.v)===v&&cpDepId(v,cpDepCommit(...(k=>[k.cx,k.cy,k.owner])(cpNoteOf(n))))===id))continue; for(let i=0;i<64;i++){const k=cpNoteOf({i,v:String(v)}); if(cpDepId(v,cpDepCommit(k.cx,k.cy,k.owner))===id){if(!cpNotes.some(n=>n.i===i))cpNotes.push({i,v:String(v),at:0,rec:1});found++;break}}} cpNotes.sort((a,b)=>a.i-b.i);cpSaveNotes();return found}; const cpFmt=v=>trimAmt(BigInt(v)*CP_SCALE,18); const cpPaint=()=>{if(!pvMode)return; const on=!!cpSeed; pvKey.innerHTML=!account?"Connect a wallet to begin.":!on?"Sign once to unlock the key that owns your deposits.": `Key unlocked <button type="button" class="fclink" data-a="backup">backup</button><button type="button" class="fclink" data-a="import">import</button><button type="button" class="fclink" data-a="recover">recover</button><button type="button" class="fclink" data-a="export">export</button>`; pvGo.textContent=!account?"Connect Wallet":!on?"Unlock key":"Deposit"; pvGo.disabled=!!cpBusy; if(!on){pvList.innerHTML="";return} const rows=cpNotes.map(n=>{const{s}=cpStatus(n),x=n.ex,ch=x?CHAINS[x.ch].name:""; const act=s==="ready"?`<button type="button" class="fclink" data-a="exit" data-i="${n.i}">exit</button>` :s==="pending"?(n.job?(n.js==="failed"?`relay failed<button type="button" class="fclink" data-a="settle" data-i="${n.i}">retry</button>`:"settling…") :`<button type="button" class="fclink" data-a="settle" data-i="${n.i}">settle</button>`) :s==="exiting"?(!x.job||x.js==="failed"?`relay failed<button type="button" class="fclink" data-a="exit" data-i="${n.i}">retry</button>` :x.self?(x.pv?`<button type="button" class="fclink" data-a="send" data-i="${n.i}">send</button>`:"proving…"):"relaying…") :s==="funded"?`<button type="button" class="fclink" data-a="activate" data-i="${n.i}">activate</button>` :s==="expired"?`<button type="button" class="fclink" data-a="reclaim" data-i="${n.i}">reclaim</button>` :s==="bridged"?`<a href="${CHAINS[x.ch].explorer}/address/${x.to}" target="_blank" rel="noreferrer">on ${ch}</a>`:s; return`<div class="cpr"><span><b>${cpFmt(n.v)} ETH</b> #${n.i}${x&&s!=="bridged"?" → "+ch:""}</span><span>${act}</span></div>`}).join(""); pvList.innerHTML=rows||`<div class="cpr"><span>No deposits yet.</span></div>`}; const pvPaint=()=>{const on=pvMode; pv.setAttribute("aria-pressed",on?"true":"false"); pv.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 [pvPanel,pvGo])el.classList.toggle("hide",!on); for(const el of [rc,rcvEl])if(el)el.classList.toggle("hide",on);document.querySelector(".card").classList.toggle("pvon",on)}; function pvSet(on){ if(on)cbDisarm(); ++seq;quoting=false; pvMode=on; if(on&&lqMode)lqSet(false); if(on&&lnMode)lnSet(false); if(on&&wnMode)wnSet(false); pvPaint(); stat.textContent="";pvNote.textContent=""; clearInterval(pvTimer); if(on){pvTimer=setInterval(()=>{if(document.hidden||!pvMode)return;pvRefresh()},8000);pvWake()} fcSync();} const pvWake=()=>{if(!account){cpSeed="";cpNotes=[]}else if(!cpSeed){const s=cpKeyFor(account);if(/^0x[0-9a-f]{64}$/i.test(s||""))cpUse(s)} if(account)pvTo.placeholder=account;cpPaint();pvRefresh()}; const pvRefresh=async()=>{if(!cpSeed||cpBusy)return;const my=++pvTick; try{await cpSync(cpNotes.some(n=>/pending|exiting/.test(cpStatus(n).s))); for(const n of cpNotes){const t=n.ex||n;if(t.job&&!["settled","failed","proven"].includes(t.js||""))await cpRelayPoll(n)} if(my===pvTick&&pvMode)cpPaint()}catch(e){try{if(my===pvTick&&pvMode)pvNote.textContent=explain(e)}catch{}}}; const pvRun=async(fn,label)=>{if(cpBusy)return;cpBusy=1;pvGo.disabled=true;stat.textContent=label||""; try{await checkWallet();await fn();cpSyncAt=0}catch(e){err(e)} cpBusy=0;cpSaveNotes();cpPaint();pvRefresh()}; pvGo.onclick=async()=>{ if(pvGo.disabled)return; if(!account)return void connect(); if(!cpSeed)return pvRun(cpUnlock,"Check your wallet for the message to sign."); pvRun(cpDeposit,"Building the deposit…")}; pvList.onclick=pvKey.onclick=e=>{const b=e.target.closest("button[data-a]");if(!b)return;sBlip(); const a=b.dataset.a,n=cpNotes.find(x=>x.i===+b.dataset.i); if(a==="backup"){prompt("Your private-bridge key. Anyone holding it can spend your deposits.",cpSeed);return} if(a==="import"){const s=(prompt("Paste a private-bridge key (0x + 64 hex), or an exported note list.")||"").trim(); if(/^\[/.test(s)){let arr;try{arr=JSON.parse(s)}catch{} if(!Array.isArray(arr))return void(stat.textContent="That is not a note list."); let add=0;for(const r of arr)if(r&&Number.isInteger(r.i)&&/^\d+$/.test(r.v||"")&&!cpNotes.some(n=>n.i===r.i)){cpNotes.push(r);add++} cpNotes.sort((a,b)=>a.i-b.i);cpSaveNotes();cpPaint();pvRefresh();stat.textContent=add?"Imported "+add+" note"+(add>1?"s":"")+".":"Nothing new in that list.";return} if(!/^0x[0-9a-fA-F]{64}$/.test(s))return void(stat.textContent="That is not a key."); try{LS[CP_KEY+account.toLowerCase()]=s.toLowerCase()}catch{}cpUse(s.toLowerCase());cpPaint();pvRefresh();return} if(a==="export"){prompt("Your note records — keep them with the key. Exit recipes cannot be rebuilt without them.",JSON.stringify(cpNotes));return} if(a==="recover")return pvRun(async()=>{const f=await cpRecover();stat.textContent=f?"Recovered "+f+" deposit"+(f>1?"s":"")+".":"Nothing new on chain for this key."},"Scanning the pool…"); if(!n)return; if(a==="settle")return pvRun(()=>cpSettleWrap(n),"Asking the relay…"); if(a==="exit")return pvRun(()=>cpExit(n),"Building the exit…"); if(a==="send")return pvRun(()=>cpSelfSend(n),"Sending the exit…"); if(a==="activate")return pvRun(()=>cpActivate(n),"Activating…"); if(a==="reclaim")return pvRun(()=>cpReclaim(n),"Reclaiming…")}; pv.onclick=()=>{sBlip();pvSet(!pvMode)}; let invOff=null; const invIcons=()=>{const o=[],seen=new Set(); for(const t of TOKENS){const k=(t.sym||"").toLowerCase(); if(!k||seen.has(k)||t.addr===ZERO)continue;seen.add(k);o.push({s:t.sym,h:t.icon,l:!!t.launched})} return o}; const invWei=n=>`<svg width="${n}" height="${n}" viewBox="0 0 16 16" fill="currentColor" fill-rule="evenodd" shape-rendering="crispEdges"><path d="M3 1h1v1H3zM5 2h1v1H5zM10 2h1v1h-1zM12 1h1v1h-1zM4 4h8v1H4zM3 5h10v1H3zM2 6h12v3H2zM3 9h10v1H3zM4 10h8v1H4zM5 11h2v1H5zM9 11h2v1H9zM2 12h3v1H2zM7 12h2v1H7zM11 12h3v1h-3zM1 13h2v1H1zM13 13h2v1h-2zM3 6h3v2H3zM10 6h3v2h-3z"/></svg>`; function invPlay(){ if(invOff)return; const host=document.querySelector(".card");if(!host)return; const W=host.clientWidth,H=host.clientHeight,COLS=6,ROWS=4,IW=22,IH=20,SW=22,SH=20; const mk=(c,h)=>{const d=document.createElement("div");d.className=c;if(h)d.innerHTML=h;return d}; const w=mk("inv");w.tabIndex=0;w.setAttribute("role","application"); w.setAttribute("aria-label","Token invaders. Arrow keys move, space fires, escape leaves."); host.appendChild(w); const hud=mk("invh");w.appendChild(hud); hud.addEventListener("pointerdown",e=>{const k=e.target.className; if(k==="invq"){e.stopPropagation();e.preventDefault();sBlip();quit()} else if(k==="invm"){e.stopPropagation();e.preventDefault();mint()} else if(k==="invsh"){e.stopPropagation();e.preventDefault();sBlip();shareMsg="";shareScore()}}); const ship=mk("invs",ETH_ICON);w.appendChild(ship); const pool=invIcons(); let al=[],bu=[],bo=[],sx=(W-SW)/2,lf=0,rt=0,sc=0,lv=3,wv=1,ov=0,dir=1,sa=0,fa=-1e9,rq=0,lt=performance.now(); let tEnd=0,minted="",minting=0,mintMsg="",shareMsg="",runId=0; const pos=(n,x,y)=>{n.style.transform=`translate(${x.toFixed(1)}px,${y.toFixed(1)}px)`}; const cur=pool.filter(t=>!t.l),lau=pool.filter(t=>t.l); const face=(r,c,i)=>{const back=r<ROWS-1; const src=back?(cur.length?cur:pool):(lau.length?lau:(cur.length?cur:pool)); const t=src.length?src[(back?i:c)%src.length]:null; return(!t||back&&i%3===0&&src.length<COLS*(ROWS-1))?{s:"WEI",h:invWei(20)}:t}; const WAVE_MS=45000; const left=()=>Math.max(0,Math.ceil((tEnd-performance.now())/1000)); const born=()=>{for(const a of al)a.node.remove();al=[]; tEnd=performance.now()+WAVE_MS; const px=(W-COLS*IW)/2; for(let r=0;r<ROWS;r++)for(let c=0;c<COLS;c++){const t=face(r,c,r*COLS+c),n=mk("invx",t.h); n.title=t.s;w.appendChild(n);al.push({node:n,s:t.s,x:px+c*IW,y:18+r*IH,c})} dir=1}; let hudWas=""; const hud2=()=>{const t=left(); const html=(ov?`<span>game over \u00b7 ${sc}</span><span class="${minted||minting?"invmsg":"invm"}">` +(minted?`${minted}`:minting?"minting\u2026":mintMsg?`${mintMsg}`:sc>0?"mint this score":"tap to play again")+`</span>`+(sc>0&&!minting?`<span class="invsh">${shareMsg||"share"}</span>`:"") :`<span>${"\u25b2".repeat(Math.max(0,lv))}</span>` +`<span${t<=10?' style="color:var(--w)"':""}>${t}s</span>` +`<span>wave ${wv} \u00b7 ${sc}</span>`) +`<span class="invq" title="Back to swapping (esc)">\u2715</span>`; if(html===hudWas)return; hudWas=html;hud.innerHTML=html}; const fire=()=>{const t=performance.now(); if(ov||bu.length||t-fa<120)return;fa=t;sPew();const n=mk("invb");w.appendChild(n); bu.push({node:n,x:sx+SW/2-1,y:H-SH-12})}; const bomb=()=>{if(!al.length)return;const m=new Map(); for(const a of al){const p=m.get(a.c);if(!p||a.y>p.y)m.set(a.c,a)} const f=[...m.values()],a=f[(Math.random()*f.length)|0],n=mk("invd"); w.appendChild(n);bo.push({node:n,x:a.x+IW/2,y:a.y+IH})}; const ht=(ax,ay,aw,ah,bx,by,bw,bh)=>ax<bx+bw&&ax+aw>bx&&ay<by+bh&&ay+ah>by; const end=()=>{ov=1;sFail();hud2()}; const step=(dt,t)=>{ if(lf)sx-=190*dt;if(rt)sx+=190*dt; sx=Math.max(0,Math.min(W-SW,sx));pos(ship,sx,H-SH-6); const per=Math.max(90,620-(COLS*ROWS-al.length)*22-(wv-1)*60); if(t-sa>per){sa=t;let e=0; for(const a of al){const nx=a.x+dir*6;if(nx<0||nx+IW>W)e=1} for(const a of al){if(e)a.y+=8;else a.x+=dir*6;pos(a.node,a.x,a.y)} if(e)dir*=-1; if(Math.random()<.5+wv*.08)bomb(); if(al.some(a=>a.y+IH>=H-SH-6))return end()} if(tEnd&&performance.now()>tEnd){tEnd=performance.now()+WAVE_MS;lv--;sFail(); ship.classList.add("pop");setTimeout(()=>ship.classList.remove("pop"),200); if(lv<=0)return end()} for(const b of bu){b.y-=260*dt;pos(b.node,b.x,b.y)} for(const b of bo){b.y+=110*dt;pos(b.node,b.x,b.y)} for(let i=bu.length-1;i>=0;i--){const b=bu[i]; if(b.y+7<0){b.node.remove();bu.splice(i,1);continue} const j=al.findIndex(a=>ht(a.x,a.y,IW-2,IH-2,b.x,b.y,2,7)); if(j>=0){const a=al[j],g=a.node;g.classList.add("pop");setTimeout(()=>g.remove(),120); al.splice(j,1);sc+=a.s==="WEI"?25:10;b.node.remove();bu.splice(i,1); if(al.length)sHit();else{sc+=100+left()*10;wv++;born();sGot()}}} for(let i=bo.length-1;i>=0;i--){const b=bo[i]; if(b.y>H){b.node.remove();bo.splice(i,1);continue} if(ht(sx,H-SH-6,SW,SH,b.x,b.y,2,6)){b.node.remove();bo.splice(i,1);lv--; ship.classList.add("pop");setTimeout(()=>ship.classList.remove("pop"),200); if(lv<=0)return end();sFail()}} hud2()}; const loop=()=>{const t=performance.now(),dt=Math.min(.05,(t-lt)/1e3);lt=t; if(!ov)step(dt,t);rq=requestAnimationFrame(loop)}; function quit(){if(rq)cancelAnimationFrame(rq);rq=0;w.remove(); document.removeEventListener("keydown",kd,true);document.removeEventListener("keyup",ku,true); invOff=null} const again=()=>{runId++;ov=0;sc=0;lv=3;wv=1;minted="";mintMsg="";shareMsg="";born();hud2()}; const shareScore=async()=>{ const what=(minted?`I scored ${sc} in zSwap invaders and claimed ${minted}` :`I scored ${sc} in zSwap invaders`)+" \u{1F47E}"; const u="https://"+SHARE_HOST+(minted?"/#score="+minted:""); const href="https://x.com/intent/post?text="+encodeURIComponent(what)+"&url="+encodeURIComponent(u); let win=null;try{win=window.open(href,"_blank")}catch{} if(win){try{win.opener=null}catch{}return} shareMsg="copied"; try{await navigator.clipboard.writeText(what+" "+u)}catch{shareMsg="could not open x.com"} hud2()}; const mint=async()=>{ if(!ov||minting||minted||!sc)return; mintMsg=""; if(!account){mintMsg="connect to mint\u2026";hud2(); try{await connect()}catch{} if(!account){mintMsg="wallet not connected";hud2();return} mintMsg=""} minting=1;hud2(); const r=runId; try{await checkWallet(); const lbl=sc+"-w"+wv+"-"+Math.random().toString(36).slice(2).padEnd(4,"0").slice(0,4); const lt=strTail(lbl),st=strTail(String(sc)); const req={from:account,to:SCOREM,value:"0x0", data:"0x"+SEL_SCLAIM+pad32("60")+encUint(96+lt.length/2)+encAddr(account)+lt+st}; const ret=await rpc(C,[req,L]); if(strip0x(ret||"").length!==64)throw Error("The minter did not answer \u2014 check the wallet is on Ethereum mainnet."); await settle(await sendTx([req])); if(r===runId){minted=lbl+".arcade.wei";sGot()}}catch(e){if(r===runId){try{console.error("zSwap mint failed:",e)}catch{} mintMsg=String(isRejection(e)?"mint cancelled":explain(e)).replace(/[<>&"'`]/g,"").slice(0,60)||"the mint failed";sFail()}} minting=0;hud2()}; function kd(e){if(e.key==="Escape"){e.preventDefault();quit();return} if(e.key==="ArrowLeft"){lf=1;e.preventDefault()} else if(e.key==="ArrowRight"){rt=1;e.preventDefault()} else if(e.key===" "||e.key==="Enter"){e.preventDefault();ov?again():fire()}} function ku(e){if(e.key==="ArrowLeft")lf=0;if(e.key==="ArrowRight")rt=0} document.addEventListener("keydown",kd,true);document.addEventListener("keyup",ku,true); let tp=0; w.addEventListener("pointerdown",e=>{tp=1;sx=Math.max(0,Math.min(W-SW,e.offsetX-SW/2));ov?again():fire()}); w.addEventListener("pointermove",e=>{if(tp)sx=Math.max(0,Math.min(W-SW,e.offsetX-SW/2))}); w.addEventListener("pointerup",()=>{tp=0}); invOff=quit;born();hud2();w.focus();sOpen();rq=requestAnimationFrame(loop); } (()=>{let tm=0,hd=0; const cn=()=>{if(tm)clearTimeout(tm);tm=0}; wn.addEventListener("pointerdown",()=>{hd=0;cn();tm=setTimeout(()=>{tm=0;hd=1;invPlay()},550)}); for(const ev of ["pointerup","pointerleave","pointercancel"])wn.addEventListener(ev,cn); wn.addEventListener("click",e=>{if(hd){e.preventDefault();e.stopPropagation();hd=0}},true); wn.addEventListener("contextmenu",e=>e.preventDefault()); })(); wnName.oninput=()=>{wnName.value=wnName.value.toLowerCase();wnQuote()}; wnTld.onchange=wnQuote; wnEnter.onclick=async()=>{ if(!account)return void connect(); if(!wnEnter.dataset.id){wnRollEl.textContent="Type a paid .wei name you own in the box above, and it can enter the roll.";return} wnEnter.disabled=true; try{ await checkWallet(); const req={from:account,to:WROLL,value:"0x0",data:"0x"+SEL_RENTER+pad32(wnEnter.dataset.id)+encUint(0)}; await rpc(C,[req,L]); await settle(await sendTx([req])); await wnRoll(); }catch(e){err(e)} wnEnter.disabled=false}; wnGo.onclick=async()=>{ if(wnGo.disabled)return; if(!account)return void connect(); wnGo.disabled=true; try{ await checkWallet(); const p=wnPend(); if(p){ if(!p.at){ if(Math.floor(Date.now()/1000)-(p.since||0)<=600)throw Error("The commitment has not confirmed yet."); wnSave(null);stat.textContent="That commitment never reached the chain. Commit again."; await wnQuote();return} const age=Math.floor(Date.now()/1000)-p.at; if(age<WN_MIN)throw Error(`Wait ${WN_MIN-age} more seconds.`); if(age>=WN_MAX){wnSave(null);stat.textContent="That commitment lapsed. Commit again.";wnQuote();return} const cost=await wnCost(p.label); const req={from:account,to:WNS,value:toHex(cost), data:"0x"+SEL_WREVEAL+pad32("40")+pad32(p.secret)+strTail(p.label)}; await rpc(C,[req,L]); await settle(await sendTx([req])); wnSave(null); stat.textContent=`${p.label}.wei is yours.`; wnName.value=""; }else if(wnQ&&wnQ.free){ const req={from:account,to:WREG,value:"0x0", data:"0x"+SEL_WSUB+pad32(IDWEI)+pad32("40")+strTail(wnQ.label)}; await rpc(C,[req,L]); await settle(await sendTx([req])); stat.textContent=`${wnQ.label}.id.wei is yours.`; wnName.value=""; }else if(wnQ){ const sec=wnSecret(); const req={from:account,to:WNS,value:"0x0", data:"0x"+SEL_WCOMMIT+pad32(wnHash(wnQ.label,account,sec))}; await rpc(C,[req,L]); wnSave({label:wnQ.label,secret:sec,at:0,since:Math.floor(Date.now()/1000)}); let h; try{h=await sendTx([req])} catch(e){ if(e&&(e.code===4001||/user (rejected|denied)|rejected the request|denied transaction/i.test(e.message||"")))wnSave(null); throw e} wnSave({label:wnQ.label,secret:sec,at:0,since:Math.floor(Date.now()/1000),tx:h}); try{await settle(h)}catch(e){if(/tx reverted/.test(e&&e.message||""))wnSave(null);throw e} await wnStamp(); stat.textContent="Committed. Register the name in a minute."; } await wnQuote();await wnRoll();refreshBalance(); }catch(e){err(e)} wnSync()}; lnGo.onclick=async()=>{ stat.textContent=""; if(lnGo.disabled)return; if(!account)return void connect(); lnGo.disabled=true; try{ await checkWallet(); if(lnKind.value==="cause")return await 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.`); const supRaw=lnSupply.value.replace(/[, ]/g,""); if(!/^\d+(\.\d+)?$/.test(supRaw)||!(Number(supRaw)>0))throw Error("Supply has to be a positive number."); const supply=parseUnits(supRaw,18); const typed=Number(lnAlloc.value||0); if(!(typed>=0)||typed*100>LN_MAX_ALLOC)throw Error("Allocation runs from 0 to 20%."); const alloc=Math.round(typed*100); if(!(alloc>=0)||alloc>LN_MAX_ALLOC)throw Error("Allocation runs from 0 to 20%."); if(typed>0&&alloc===0)throw Error("Allocation is priced in whole basis points — the smallest non-zero value is 0.01%."); const mcap=parseUnits(String(lnMcap.value||"").trim(),18); if(mcap<LN_MIN_MCAP)throw Error("That opening valuation is too small to price."); const pooled=supply-supply*BigInt(alloc)/10000n; if(pooled<LN_MIN_POOLED)throw Error("Too little supply reaches the pool to price a market."); if(pooled>mcap*10n**36n)throw Error("That supply is too large for that opening valuation \u2014 raise the valuation."); if(pooled*10n**24n<mcap)throw Error("That opening valuation is too large for that supply \u2014 lower it, or mint more."); const owner=await lnOwner(); stat.textContent="Launching\u2026"; const req={from:account,to:PLAUNCH,value:"0x0", data:encLaunch(name,sym,"",supply,BigInt(alloc),mcap,owner,lnArtBytes,lnArtMime)}; const sim=await rpc(C,[req,L]); const candidate=/^0x[0-9a-fA-F]{128}$/.test(sim||"")?"0x"+strip0x(sim).slice(24,64):null; const receipt=await waitTx(await sendTx([req]));sGot(); lnSet(false); lnName.value="";lnSym.value="";lnSupply.value="";lnAlloc.value=""; const topic=kecStr("Launched(address,address,address,uint256,uint256,uint256)"); const lg=(receipt.logs||[]).find(l=>(l.address||"").toLowerCase()===PLAUNCH.toLowerCase() &&(l.topics||[])[0]&&l.topics[0].toLowerCase()===topic.toLowerCase()); let token=lg?"0x"+strip0x(lg.topics[1]).slice(24):null;if(token&&!isAddr(token))token=null; if(!token&&candidate){ try{ const c=await rpc(C,[{to:PLAUNCH,