memoscan
← all memos

Memo 0x44d4347d…5bdefa on Ethereum

0x68575b07…67c7·#25,913,254·view on Etherscan
const minLp=lessSlip(pv.lp); const zap="0x"+SEL_ZAPIN+encAddr(pool)+encAddr(tok.addr)+encUint(amountIn) +encUint(pv.portion)+encUint(minLp)+encAddr(to)+encAddr(account); const leg=encSnwap(tok.addr,amountIn,to,pool,minLp,PROUTE,zap); let data,value; if(tok.addr===ZERO){data=leg;value=toHex(amountIn)} else{ stat.textContent="Approving…"; await ensureAllowance(tok.addr,ZROUTER,amountIn); data=encCalls([ encSnwap(ZERO,0n,account,ZERO,0n,PROUTE, "0x"+SEL_PCHECKPOINT+encAddr(tok.addr)+strip0x(keccak(hexToBytes(zap)))+encAddr(account)), leg]); value="0x0"; } stat.textContent="Zapping…"; const req={from:account,to:ZROUTER,value,data}; await rpc(C,[req,L]); await settle(await sendTx([req])); await loadLq();refreshBalance(); }catch(e){err(e)} finally{--busy} } let lqPvT,lqPvSeq=0; async function lqPreview(box){ const pool=box.closest(".lqrow")?.dataset.pool||""; const row=lqRows.find(x=>x.pool.toLowerCase()===pool.toLowerCase()); const p=chPair(); const out=box.querySelector(".lqpv"),go=box.querySelector('[data-act="ac"]'); if(!row||!p){out.textContent="";go.disabled=true;return} const [t0,t1]=p,ins=box.querySelectorAll(".lqin"); let a0,a1; try{ a0=ins[0].value.trim()?parseUnits(ins[0].value.trim(),t0.dec):0n; a1=ins[1].value.trim()?parseUnits(ins[1].value.trim(),t1.dec):0n; }catch{out.textContent="Invalid amount.";go.disabled=true;return} if(isZap(box)){ const z=zapSide(box,t0,t1); if(z.err){out.textContent=z.err;go.disabled=true;return} if(!z.amt){out.textContent=`Enter an amount of ${t0.sym} or ${t1.sym}.`;go.disabled=true;return} const mz=++lqPvSeq; out.textContent="Checking…";go.disabled=true; const zp=await previewZap(row.pool,z.tok.addr,z.amt).catch(()=>null); if(mz!==lqPvSeq)return; if(!zp||zp.lp===0n){out.textContent="The pool would refuse this deposit.";go.disabled=true;return} out.textContent=`swaps ${trimAmt(zp.portion,z.tok.dec)} of ${trimAmt(z.amt,z.tok.dec)} ${z.tok.sym}` +` for ${(z.tok===t0?t1:t0).sym}, then deposits both · mints ~${trimAmt(zp.lp,18)} LP`; go.disabled=false;return; } if(!a0||!a1){ out.textContent="Enter both amounts.";go.disabled=true;return} const my=++lqPvSeq; out.textContent="Checking…";go.disabled=true; const pv=await previewAdd(row.pool,a0,a1).catch(()=>null); if(my!==lqPvSeq)return; if(!pv){out.textContent="The pool would refuse this deposit.";go.disabled=true;return} const ref=(pv.ref0>0n?` · refunds ${trimAmt(pv.ref0,t0.dec)} ${t0.sym}`:"") +(pv.ref1>0n?` · refunds ${trimAmt(pv.ref1,t1.dec)} ${t1.sym}`:""); out.textContent=`deposits ${trimAmt(pv.used0,t0.dec)} ${t0.sym} + ${trimAmt(pv.used1,t1.dec)} ${t1.sym}${ref}`; go.disabled=false; } const isZap=box=>!!box.querySelector(".lqz")?.checked; function zapSide(box,t0,t1){ const ins=box.querySelectorAll(".lqin"); const v0=ins[0].value.trim(),v1=ins[1].value.trim(); if(v0&&v1)return{err:`One side only — clear the ${t1.sym} field, or turn one-sided off.`}; if(!v0&&!v1)return{err:`Enter an amount of ${t0.sym} or ${t1.sym}.`}; const tok=v0?t0:t1; try{return{tok,amt:parseUnits(v0||v1,tok.dec)}}catch{return{err:"Invalid amount."}} } function lqMirror(box,src){ if(isZap(box))return; const pool=box.closest(".lqrow")?.dataset.pool||""; const row=lqRows.find(x=>x.pool.toLowerCase()===pool.toLowerCase()); const p=chPair(); if(!row||!p||!row.r0||!row.r1)return; const [t0,t1]=p,ins=box.querySelectorAll(".lqin"); const i=src===ins[0]?0:src===ins[1]?1:-1; if(i<0)return; const other=ins[1-i],v=src.value.trim(); box.dataset.mir=String(1-i); if(!v){other.value="";return} try{ const raw=parseUnits(v,i===0?t0.dec:t1.dec); other.value=formatUnits(i===0?raw*row.r1/row.r0:raw*row.r0/row.r1,i===0?t1.dec:t0.dec); }catch{} } lqList.addEventListener("input",e=>{ const box=e.target.closest(".lqadd"); if(box&&e.target.classList.contains("lqin")){ box.querySelector('[data-act="ac"]').disabled=true; lqMirror(box,e.target);clearTimeout(lqPvT);lqPvT=setTimeout(()=>lqPreview(box),300);return} const nb=e.target.closest(".lqnew"); if(nb){nb.querySelector("#lqCreate").disabled=true; clearTimeout(lqPvT);lqPvT=setTimeout(lqNewPreview,300)} }); lqList.addEventListener("change",e=>{ if(e.target.id==="lqFee"||e.target.id==="lqRange")return lqNewPreview(); if(!e.target.classList.contains("lqz"))return; const box=e.target.closest(".lqadd"),ins=box.querySelectorAll(".lqin"); const on=e.target.checked; const mir=box.dataset.mir; if(on&&mir&&ins[0].value.trim()&&ins[1].value.trim())ins[+mir].value=""; if(!on)lqMirror(box,ins[0].value.trim()||!ins[1].value.trim()?ins[0]:ins[1]); box.querySelector('[data-act="ac"]').textContent=on?"Zap in":"Add liquidity"; lqPreview(box); }); let lqNewSeq=0; let lqPvT2; const lqPreviewSoon=()=>{const g=lqList.querySelector("#lqCreate");if(g)g.disabled=true; clearTimeout(lqPvT2);lqPvT2=setTimeout(lqNewPreview,320)}; async function lqNewPreview(){ const box=lqList.querySelector(".lqnew"); if(!box)return; const out=box.querySelector("#lqPv"),go=box.querySelector("#lqCreate"); const bandOut=box.querySelector("#lqRangeOut"); const my=++lqNewSeq; go.disabled=true; const pair=chPair(); if(!pair){out.textContent="";return} const [t0,t1]=pair; const range=box.querySelector("#lqRange").value; box.querySelector("#lqCustom").classList.toggle("hide",range!=="custom"); const payTok=TOKENS[fromSel.value]; const payIs0=!!payTok&&payTok.addr.toLowerCase()===t0.addr.toLowerCase(); const el0=payIs0?amt:outAmt,el1=payIs0?outAmt:amt; let a0=0n,a1=0n; try{ const v0=el0.value.trim(),v1=el1.value.trim(); a0=v0?parseUnits(v0,t0.dec):0n;a1=v1?parseUnits(v1,t1.dec):0n; }catch{out.textContent="Check the amounts.";bandOut.textContent="";return} const oneSided=(a0&&!a1)?0:(a1&&!a0)?1:-1; box.querySelector("#lqPxRow").classList.toggle("hide",oneSided<0); if(!a0&&!a1){ bandOut.textContent=""; out.textContent=`Enter how much ${t0.sym} and ${t1.sym} to deposit — the ratio sets the opening price.`; return} const pxIn=box.querySelector("#lqPx").value.trim(); const px=oneSided<0 ?(Number(a1)/10**t1.dec)/(Number(a0)/10**t0.dec) :(pxIn?+pxIn:NaN); if(!(px>0)){ bandOut.textContent=""; const only=oneSided===0?t0.sym:t1.sym; out.textContent=`${/^[AEIOU]/i.test(only)?"An":"A"} ${only}-only band has no ratio to` +` read a price from — name one.`; return} const fee=BigInt(box.querySelector("#lqFee").value); let lo,hi; if(range==="custom"){ const n=id=>{const v=box.querySelector("#"+id).value.trim();return v?+v:NaN}; lo=n("lqLo");hi=n("lqHi"); if(!(lo>0)||!(hi>0)){bandOut.textContent="";out.textContent="Name both prices.";return} if(lo>=hi){out.textContent="The low price must be below the high one.";bandOut.textContent="";return} if(oneSided<0&&(px<lo||px>hi)){ out.textContent=`These amounts imply ${fmtPx(px)}, which is outside that band.`;return} }else{ let mult=+range; if(range==="full"){ const found=lqFullWidth(t0.dec,t1.dec,a0,a1,px,fee); if(!found){ bandOut.textContent=""; out.textContent="Even the narrowest range needs more than this deposit.";return} mult=found; } [lo,hi]=lqBandFor(px,mult,a0,a1); } const sl=pxToSqrt(lo,t0.dec,t1.dec),sh=pxToSqrt(hi,t0.dec,t1.dec); if(!sl||!sh||sh<=sl){out.textContent="Check the prices.";bandOut.textContent="";return} const sp=oneSided===0?sl:oneSided===1?sh:lqSeedPrice(sl,sh,a0,a1); if(!sp){out.textContent="Those amounts cannot open a band that wide.";return} const openAt=bandPrice(sp,t0.dec,t1.dec); bandOut.innerHTML=lqBar(lo,hi,openAt)+`<span></span>`; bandOut.querySelector("span").textContent= `${fmtPx(lo)} – ${fmtPx(hi)} · opens at ${fmtPx(openAt)} ${t1.sym} per ${t0.sym}` +(oneSided===0?` · ${t0.sym} only`:oneSided===1?` · ${t1.sym} only`:""); box.querySelector("#lqOwnNote").textContent= `Unowned, like any AMM pool: anyone may add, and no creator fee can ever be` +` charged. Prices are ${t1.sym} per ${t0.sym}.`; lqGasNote(box); const market=lqMarket(t0.addr,t1.addr,sl,sh,fee,ZERO); box.dataset.sl=sl;box.dataset.sh=sh;box.dataset.sp=sp;box.dataset.fee=fee; box.dataset.a0=a0;box.dataset.a1=a1;box.dataset.lp=0; const q=lqSeedQuote(sl,sh,sp,a0,a1); if(!q.ok){out.textContent="This band cannot open at that price with those amounts.";return} if(!lqSeedAdmits(sl,sh,sp,fee,q)){ out.textContent="A band this size could not be traded once opened —" +" widen the range, or deposit more.";return} out.textContent=`Seeds ${trimAmt(q.used0,t0.dec)} ${t0.sym} + ${trimAmt(q.used1,t1.dec)} ${t1.sym}` +((q.used0<a0||q.used1<a1)?" — the rest is returned":""); box.dataset.lp=q.lp; try{ const [addr]=await mc3([{to:PFACTORY,data:"0x"+SEL_POOLFOR+market}]); if(my!==lqNewSeq)return; const pool="0x"+strip0x(addr).slice(24); const [sup]=await mc3([{to:pool,data:"0x"+SEL_TOTALSUPPLY}]).catch(()=>[null]); if(my!==lqNewSeq)return; if(sup&&strip0x(sup).length>=64&&dec1(sup,0)>0n){ out.textContent="That exact band already exists — add to it from the list above."; return} go.disabled=!account; }catch{ if(my!==lqNewSeq)return; out.textContent="Could not reach the factory."; } } const LQ_CREATE_GAS=4700000n; async function lqGasNote(box){ const el=box.querySelector("#lqGas"); if(!el)return; el.textContent=`Creating deploys the pool contract — about ` +`${(Number(LQ_CREATE_GAS)/1e6).toFixed(1)}M gas.`; try{ const wei=BigInt(await rpc("eth_gasPrice",[]))*LQ_CREATE_GAS; el.textContent=`Creating deploys the pool contract — about ` +`${(Number(LQ_CREATE_GAS)/1e6).toFixed(1)}M gas, roughly ` +`${trimAmt(wei,18)} ETH at the moment. Adding to a band that already ` +`exists costs a fraction of it, so check the fee your wallet offers.`; }catch{} } async function lqCreate(){ const box=lqList.querySelector(".lqnew"); if(!box||busy||!account)return; const pair=chPair(); if(!pair)return; const [t0,t1]=pair; const go=box.querySelector("#lqCreate"),out=box.querySelector("#lqPv"); const sl=BigInt(box.dataset.sl||0),sh=BigInt(box.dataset.sh||0),sp=BigInt(box.dataset.sp||0); const a0=BigInt(box.dataset.a0||0),a1=BigInt(box.dataset.a1||0),fee=BigInt(box.dataset.fee||0); if(!sl||!sh||!sp){out.textContent="Preview the band first.";return} const to=await lqTo(); if(!to)return; ++busy;go.disabled=true; try{ const bps=BigInt(Math.round(Math.min(.5,Math.max(.01,+slip.value||.5))*100)); const lp=BigInt(box.dataset.lp||0); if(lp<=0n){out.textContent="Preview the band first.";return} const minLP=lp*(10000n-bps)/10000n; const market=lqMarket(t0.addr,t1.addr,sl,sh,fee,ZERO); const native=t0.addr===ZERO; if(!native&&a0)await lqApprove(t0.addr,a0); if(a1)await lqApprove(t1.addr,a1); const data="0x"+SEL_CREATE_SEED+market+encUint(sp)+encUint(a0)+encUint(a1) +encUint(minLP)+encAddr(to); const tx={from:account,to:PFACTORY,data,value:native?toHex(a0):"0x0"}; stat.textContent="Creating the band..."; await rpc(C,[tx,L]);await checkWallet();await settle(await sendTx([tx])); stat.textContent="Band created."; loadLq();refreshBalance(); }catch(e){err(e);out.textContent="";} finally{--busy;go.disabled=false} } async function lqApprove(token,need){ const al=BigInt(await rpc(C,[{to:token,data:"0x"+SEL_ALLOWANCE+encAddr(account)+encAddr(PFACTORY)},L])); if(al>=need)return; if(al)await settle(await sendTx([{from:account,to:token, data:"0x"+SEL_APPROVE+encAddr(PFACTORY)+encUint(0n),value:"0x0"}])); await settle(await sendTx([{from:account,to:token, data:"0x"+SEL_APPROVE+encAddr(PFACTORY)+encUint(need),value:"0x0"}])); } lqList.onclick=async ev=>{ if(ev.target.id==="lqCreate"){await lqCreate();return} if(ev.target.id==="lqNewTog"){ const pr=chPair();if(!pr)return; ev.target.outerHTML=lqNewForm(pr[0],pr[1]); lqSyncAmounts(); lqNewPreview(); lqList.querySelector(".lqnew")?.scrollIntoView?.({block:"nearest"}); return} const b=ev.target.closest("[data-act]"); if(!b||busy)return; const p=chPair(); if(!p)return; const [t0,t1]=p,pool=b.dataset.pool; const row=lqRows.find(x=>x.pool.toLowerCase()===pool.toLowerCase()); if(!row)return; const act=b.dataset.act; if(act!=="a"&&!account)return void(stat.textContent="Connect a wallet first."); if(act==="a"){ const box=b.closest(".lqrow").querySelector(".lqadd"); box.classList.toggle("hide"); b.textContent=box.classList.contains("hide")?"Add":"Cancel"; if(!box.classList.contains("hide"))box.querySelector(".lqin").focus(); return; } if(act==="w"){ if(row.shares===0n)return; ++busy;b.disabled=true; try{await doRemove(pool,row.shares,t0,t1)}finally{--busy;b.disabled=false} return; } if(act==="ac"){ const box=b.closest(".lqadd"); const ins=box.querySelectorAll(".lqin"); if(isZap(box)){ const z=zapSide(box,t0,t1); if(z.err){stat.textContent=z.err;return} if(!z.amt)return; ++busy;b.disabled=true; try{await doZap(pool,z.tok,z.amt)}finally{--busy;b.disabled=false} return; } let a0,a1; try{ a0=parseUnits(ins[0].value.trim(),t0.dec);a1=parseUnits(ins[1].value.trim(),t1.dec); }catch{stat.textContent="Invalid amount";return} if(!a0||!a1)return; ++busy;b.disabled=true; try{await doAdd(pool,a0,a1,t0,t1)}finally{--busy;b.disabled=false} } }; footDoc.setAttribute("aria-expanded","false"); footDoc.onclick=e=>{ e.preventDefault(); const on=docPanel.classList.contains("hide"); footDoc.setAttribute("aria-expanded",on?"true":"false"); docPanel.classList.toggle("hide",!on); footDoc.textContent=on?"hide":"how it works"; if(on)docPanel.scrollIntoView({behavior:"smooth",block:"nearest"}); }; lq.onclick=()=>{sBlip();lqSet(!lqMode)}; let PLAUNCH=MB.la; const SEL_LAUNCH="6a648dc6"; const SEL_LAUNCHART="72cf7bd8"; const SEL_CREATOROF="dea5c2e0"; const SEL_FLOORPRICE="2aad9987"; const ETHUSD_FEED="0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419"; const ETHUSD_STALE=3600; let ethUsd=0; async function loadEthUsd(){ try{ const c=JSON.parse(sessionStorage.getItem("ethusd")||"null"); if(c&&Date.now()-c.t<300000){ethUsd=c.v;return} }catch{} try{ const r=await cfgRead(C,[{to:ETHUSD_FEED,data:"0xfeaf968c"},L]); const h=strip0x(r||""); if(h.length<5*64)return; const answer=BigInt("0x"+h.slice(64,128)),updated=Number(BigInt("0x"+h.slice(192,256))); if(answer<=0n)return; if(Math.floor(Date.now()/1000)-updated>ETHUSD_STALE)return; ethUsd=Number(answer)/1e8; try{sessionStorage.setItem("ethusd",JSON.stringify({t:Date.now(),v:ethUsd}))}catch{} }catch{} } const fmtUsd=v=>{ if(!(v>0))return ""; if(v>=1000)return "$"+Math.round(v).toLocaleString("en-US"); if(v>=1)return "$"+v.toFixed(2); if(v>=0.01)return "$"+v.toFixed(4); return "$"+fmtPx(v); }; const BETH="0x2cb662Ec360C34a45d7cA0126BCd53C9a1fd48F9"; const TITHE_RECORD="0x5E58BA0e06ED0F5558f83bE732a4b899a674053E"; const BURN_TTL=600000; async function burnTotal(){ try{ const c=JSON.parse(sessionStorage.getItem("burn")||"null"); if(c&&Date.now()-c.t<BURN_TTL)return void showBurn(BigInt(c.v)); }catch{} try{ const r=await cfgRead(C,[{to:BETH,data:"0x70a08231"+encAddr(TITHE_RECORD)},L]); const v=BigInt(r||"0x0"); if(v<=0n)return; try{sessionStorage.setItem("burn",JSON.stringify({t:Date.now(),v:v.toString()}))}catch{} showBurn(v); }catch{} } function showBurn(v){ if(v<=0n)return; footBurn.innerHTML=` \u00b7 <a href="https://etherscan.io/token/${BETH}?a=${TITHE_RECORD}" ` +`target="_blank" rel="noopener" title="A tenth of every fee a launched coin earns is burned to Ethereum itself. ` +`This is the running total, held as the BETH receipt minted to the DAO - the ether it stands for no longer exists.">` +`${(+formatUnits(v,18)).toFixed(4)} ETH burned</a>`; } async function quoteFloor(tok,amount,block){ if(!tok||tok===ZERO||amount<=0n)return 0n; const key=tok.toLowerCase(); const at=block||L; if(!fcFloorPx[key]||fcFloorPx[key].blk!==at){ try{ const r=await rpc(C,[{to:PLAUNCH,data:"0x"+SEL_FLOORPRICE+encAddr(tok)},at]); fcFloorPx[key]={v:BigInt(r||"0x0"),blk:at}; }catch{delete fcFloorPx[key];return 0n} } const px=fcFloorPx[key].v; return px>0n?px*amount/(10n**18n):0n; } const SEL_POOLOF="988b1fa7",SEL_OWED0="56ea33ee",SEL_OWED1="3ad57b72",SEL_COLLECT="a480ca79"; const SEL_COLLECTMANY="c296057e"; let fcMineToks=[],fcSeq=0,fcMineSeq=0; async function fcMine(){ const my=++fcMineSeq; fcMineToks=[]; if(!account||!LAUNCHED.length||PLAUNCH===ZERO)return fcSync(); try{ const reqs=[]; for(const x of LAUNCHED)reqs.push( {to:PLAUNCH,data:"0x"+SEL_CREATOROF+encAddr(x.addr)}, {to:x.pool,data:"0x"+SEL_OWED0}); const res=await mc3Deep(reqs); if(my!==fcMineSeq)return; const acct=account.toLowerCase(); let total=0n;const mine=[]; for(let i=0;i<LAUNCHED.length;i++){ const c=res[i*2],o=res[i*2+1]; if(!c||!o)continue; if(("0x"+strip0x(c).slice(24)).toLowerCase()!==acct)continue; let owed=0n;try{owed=BigInt(o)}catch{continue} if(owed<FC_DUST)continue; total+=owed;mine.push(LAUNCHED[i]); } if(!mine.length)return fcSync(); fcMineToks=mine.map(x=>x.addr); const cut=(+formatUnits(total*8n/10n,18)).toFixed(4); fcEl.innerHTML=(mine.length===1?`${safeSym(mine[0].sym)} \u00b7 `:`${mine.length} of your coins \u00b7 `) +`${cut} ETH in creator fees ` +`<button type="button" id="fcGo" class="fclink">${mine.length===1?"Collect":"Collect all"}</button>`; fcEl.title="Trading fees your launched coins have earned and nobody has swept yet. " +"The ether comes to you; the token side is burned, which raises the floor for your holders."; fcEl.classList.toggle("hide",lnMode||lqMode||wnMode||pvMode||tab!=="swap"); fcGo.onclick=fcCollectMine; }catch{if(my===fcMineSeq)fcSync()} } async function fcCollectMine(){ if(!fcMineToks.length)return; const b=fcGo;if(b.disabled)return; const label=b.textContent; try{ b.disabled=true;b.textContent="Collecting\u2026"; const req={from:account,to:PLAUNCH,value:"0x0", data:"0x"+SEL_COLLECTMANY+encUint(32)+encUint(BigInt(fcMineToks.length)) +fcMineToks.map(encAddr).join("")}; await rpc(C,[req,L]); await waitTx(await sendTx([req]));sGot(); stat.textContent="Fees collected."; for(const k of Object.keys(fcFloorPx))delete fcFloorPx[k]; fcMine(); }catch(e){err(e);b.disabled=false;b.textContent=label} } const FC_DUST=10000000000000n; let fcTok="",fcOwed=0n,fcOwner=""; const fcPool={},fcFloorPx={}; async function fcSync(){ const my=++fcSeq; const t=TOKENS[toSel.value],f=TOKENS[fromSel.value]; const cand=[t,f].find(x=>x&&x.addr&&x.addr!==ZERO&&x.std!=="nft"); fcTok="";fcOwed=0n; if(lnMode||lqMode||wnMode||pvMode||tab!=="swap"||PLAUNCH===ZERO){fcEl.classList.add("hide");return} if(fcMineToks.length){fcEl.classList.remove("hide");return} if(!cand){fcEl.classList.add("hide");return} try{ const key=cand.addr.toLowerCase(); let pool=fcPool[key]; if(pool===undefined){ const pr=await rpc(C,[{to:PLAUNCH,data:"0x"+SEL_POOLOF+encAddr(cand.addr)},L]); if(my!==fcSeq)return; pool=fcPool[key]="0x"+strip0x(pr||"").slice(24); } if(!/^0x[0-9a-f]{40}$/i.test(pool)||/^0x0{40}$/i.test(pool)){fcEl.classList.add("hide");return} const [o0,cr]=await mc3Deep([{to:pool,data:"0x"+SEL_OWED0}, {to:PLAUNCH,data:"0x"+SEL_CREATOROF+encAddr(cand.addr)}]); if(my!==fcSeq)return; fcOwner=cr?"0x"+strip0x(cr).slice(24):""; const owed=BigInt(o0||"0x0"); if(owed<FC_DUST){fcEl.classList.add("hide");return} fcTok=cand.addr;fcOwed=owed; const mine=account&&fcOwner&&account.toLowerCase()===fcOwner.toLowerCase(); fcEl.innerHTML=`${(+formatUnits(owed*8n/10n,18)).toFixed(4)} ETH in ` +`${mine?"creator fees":"fees for this coin's creator"} ` +`<button type="button" id="fcGo" class="fclink">Collect</button>`; fcEl.title=mine?"Trading fees this coin has earned. Anyone may collect them and the ether comes to you." :"Trading fees this coin has earned. Anyone may collect them; the ether goes to the coin's creator " +"either way, and the token side is burned, which raises the floor for holders."; fcEl.classList.remove("hide"); fcGo.onclick=fcCollect; }catch{if(my===fcSeq)fcEl.classList.add("hide")} } async function fcCollect(){ if(!fcTok)return; const b=fcGo;if(b.disabled)return; try{ if(!account)return connect(); b.disabled=true;b.textContent="Collecting\u2026"; const req={from:account,to:PLAUNCH,value:"0x0",data:"0x"+SEL_COLLECT+encAddr(fcTok)}; await rpc(C,[req,L]); await waitTx(await sendTx([req]));sGot(); stat.textContent="Fees sent to the creator."; for(const k of Object.keys(fcFloorPx))delete fcFloorPx[k]; fcSync(); }catch(e){err(e);b.disabled=false;b.textContent="Collect"} } const SEL_DAOOF="98fabd3a",SEL_LOOTOF="9b7b2ab0",SEL_SHARESOF="03314efa", SEL_TSUP="18160ddd",SEL_RAGEQUIT="29f64d1a"; const SEL_SALES="c6b9f06a",SEL_REMAINING="b399b0bc",SEL_BUY="cce7ec13"; const SEL_TAPS="6144452a",SEL_TAPCLAIM="1e83409a",SEL_DAOALLOW="dd62ed3e"; const SEL_RQABLE="14a6d7de"; const cbSeen={}; let cbDao="",cbAmt=0n,cbCost=0n,cbBuy=false,cbDrawn=0,cbSold=0n,cbReady=0n,cbSeq=0; let cbDue=0n,cbShares="",cbTok=""; const cbDisarm=()=>{cbSeq++;cbDao="";cbAmt=0n;cbReady=0n;cbDue=0n;cbShares="";cbTok=""; if(typeof cbEl!=="undefined"&&cbEl)cbEl.classList.add("hide")}; const cbWord=h=>{const b=strip0x(h||"");return b.length>=64?"0x"+b.slice(24,64):""}; async function cbLook(tok){ const key=tok.toLowerCase(); if(cbSeen[key])return cbSeen[key]; let out={dao:"",shares:""}; try{ const dao=cbWord(await rpc(C,[{to:tok,data:"0x"+SEL_DAOOF},L])); if(/^0x[0-9a-f]{40}$/i.test(dao)&&!/^0x0{40}$/i.test(dao)){ const [lt,sh]=await mc3Deep([{to:dao,data:"0x"+SEL_LOOTOF},{to:dao,data:"0x"+SEL_SHARESOF}]); if(lt==null||sh==null)return out; if(cbWord(lt).toLowerCase()===key){out={dao,shares:cbWord(sh)}; const ix=TOKENS.findIndex(t=>t.addr&&t.addr.toLowerCase()===key); if(ix>=0&&!TOKENS[ix].cause){TOKENS[ix].cause=1;rebuild()}} } }catch{ return out; } return cbSeen[key]=out; } async function cbSync(){ const my=++cbSeq; cbDao="";cbAmt=0n;cbBuy=false; const f=TOKENS[fromSel.value],t=TOKENS[toSel.value]; if(lnMode||lqMode||wnMode||pvMode||tab!=="swap"||!f||!t){cbEl.classList.add("hide");return} const real=x=>x&&x.addr&&x.addr!==ZERO&&x.std!=="nft"; const cbEth=f.addr===ZERO; let hit=null,tok=null,buy=false; if(real(t)){const h=await cbLook(t.addr);if(my!==cbSeq)return; if(h.dao){hit=h;tok=t;buy=true}} if(!hit&&real(f)){const h=await cbLook(f.addr);if(my!==cbSeq)return; if(h.dao){hit=h,tok=f}} if(!hit||!hit.shares||!tok){cbEl.classList.add("hide");return} let eth=0n,sup=0n,price=0n,dl=0n,left=0n,cap=0n; let rqOff=false; const now2=BigInt(Math.floor(Date.now()/1000)); cbReady=0n; try{ const [b,r]=await Promise.all([ rpc("eth_getBalance",[hit.dao,L]), mc3Deep([{to:hit.shares,data:"0x"+SEL_TSUP},{to:tok.addr,data:"0x"+SEL_TSUP}, {to:COFF,data:"0x"+SEL_SALES+encAddr(hit.dao)}, {to:COFF,data:"0x"+SEL_REMAINING+encAddr(hit.dao)}, {to:CTAP,data:"0x"+SEL_TAPS+encAddr(hit.dao)}, {to:hit.dao,data:"0x"+SEL_DAOALLOW+encAddr(ZERO)+encAddr(CTAP)}, {to:hit.dao,data:"0x"+SEL_RQABLE}])]); eth=BigInt(b||"0x0"); if(my!==cbSeq)return; if(r[0]==null||r[1]==null||r[2]==null||r[3]==null){cbEl.classList.add("hide");return} const shr=BigInt(r[0]),lot=BigInt(r[1]); sup=shr+lot; const sale=strip0x(r[2]||""); if(sale.length>=320 &&("0x"+sale.slice(24,64)).toLowerCase()===CLOOT &&/^0+$/.test(sale.slice(64,128))){ dl=BigInt("0x"+sale.slice(128,192));price=BigInt("0x"+sale.slice(192,256)); cap=BigInt("0x"+sale.slice(256,320))} cbSold=lot; left=BigInt(r[3]||"0x0"); const tp=strip0x(r[4]||""); if(tp.length>=256){ const rate=BigInt("0x"+tp.slice(128,192)),lastC=BigInt("0x"+tp.slice(192,256)); const allow=BigInt(r[5]||"0x0"); if(rate>0n&&now2>lastC){ let av=(now2-lastC)*rate; if(av>allow)av=allow; if(av>eth)av=eth; cbReady=av/rate*rate; }} rqOff=r[6]!=null&&BigInt(r[6])===0n; cbDrawn=lot>0n&&price>0n?(()=>{const raised=(lot*price+CONE-1n)/CONE; return raised>eth?Number((raised-eth)*100n/raised):0})():0; }catch{} if(my!==cbSeq)return; if(sup<=0n){cbEl.classList.add("hide");return} const now=BigInt(Math.floor(Date.now()/1000)); const open=price>0n&&(dl===0n||dl>now); const days=dl>now?Number((dl-now+86399n)/86400n):0; cbDao=hit.dao;cbBuy=buy; const pct=cap>0n?Number(cbSold*1000n/cap)/10:0; const goal=cap>0n&&price>0n?cap*price/CONE:0n; const head=goal>0n ?`<span>${trimAmt(cbSold*price/CONE,18)} of ${trimAmt(goal,18)} ETH backed</span>` +`<span>${open?days+" days left":"closed"}${cbDrawn>0?" · "+cbDrawn+"% released":""}</span>` :