0x68575b07…67c7·#25,913,250·view on Etherscan
lipBps>slipBps)rate.textContent+=` · Split — bound widened to ${Number(r.slipBps)/100}%`;
if(bookIncomplete)rate.textContent+=" · Book scan capped";
if(plannerBounded)rate.textContent+=" · Book split approximate";
if(bookUnavailable)rate.textContent+=" · AMM only (books offline)";
if(pq&&pq.capped)rate.textContent+=` · Precision scan capped (${LQ_SCAN})`;
if(isIn&&f.launched&&(t.addr===ZERO||t.addr.toLowerCase()===WETH.toLowerCase())){
const floorOut=await quoteFloor(f.addr,swapAmt,quoteBlock);
if(my!==seq)return;
if(floorOut>0n){
if(floorOut>finalOut){
rate.textContent+=` \u00b7 Floor pays more: ${trimAmt(floorOut,18)} ETH`;
rate.title=`This coin can be burned back to the launcher for its share of the ether `
+`locked in its pool - ${trimAmt(floorOut,18)} ETH, against ${trimAmt(finalOut,18)} from selling. `
+`Redeem through the launcher at ${PLAUNCH} to take the larger.`;
}else{
rate.textContent+=` \u00b7 Floor ${trimAmt(floorOut,18)} ETH`;
rate.title=`The ether backing this amount if it were redeemed instead of sold. `
+`Selling pays more right now.`;
}
}
}
let refQ=iRef>=0&&dec[iRef]?dec[iRef].best:null;
let refPair=null;
if(!refQ&&isIn&&pq&&pq.out>0n&&refAmt>0n){
try{
const small=await quotePrecision(f,t,refAmt,PROUTE,quoteBlock);
if(my!==seq)return;
if(small&&small.out>0n)refPair={amountOut:small.out};
}catch{}
}
if(my!==seq)return;
const impSrc=refQ||refPair;
const imp=impSrc?impactBps(isIn?finalOut:finalIn,isIn?impSrc.amountOut:impSrc.amountIn,!isIn):null;
const mkt=impSrc?(isIn?impSrc.amountOut:impSrc.amountIn)*REF_N:0n;
const lossTok=imp!==null&&mkt?(isIn?(mkt>finalOut?mkt-finalOut:0n):(finalIn>mkt?finalIn-mkt:0n)):0n;
if(imp!==null&&imp>=IMPACT_HIDE){
const pctTxt=(Number(imp)/100).toFixed(2);
rate.textContent+=` · Impact ${pctTxt}%`;
if(imp>=IMPACT_WARN)stat.textContent=`High price impact: ${pctTxt}% — about ${trimAmt(lossTok,isIn?t.dec:f.dec)} ${isIn?t.sym:f.sym} worse than the market rate.`;
}
if(rankBox.firstChild){
const car=document.createElement("span");
car.className="rk";
car.textContent="\u25be";
car.title="Compare venues";
car.setAttribute("role","button");
car.tabIndex=0;
car.setAttribute("aria-expanded",rankBox.classList.contains("hide")?"false":"true");
const carToggle=()=>{const shut=rankBox.classList.toggle("hide");
rankOpen=!shut;
car.setAttribute("aria-expanded",shut?"false":"true")};
car.onclick=carToggle;
car.onkeydown=e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();carToggle()}};
rate.appendChild(document.createTextNode(" "));
rate.appendChild(car);
}
const inApprove=isIn?swapAmt:r.msgValue||r.amountLimit;
const shownPay=amt.value,shownGet=outAmt.value;
if(acct)last={callData:r.callData,fundedCallData:r.fundedCallData,msgValue:r.msgValue,
limit:r.amountLimit,shownPay,shownGet,
to:r.to,spender:r.spender,
amountIn:inApprove,from:f,recipient:rcv||"",exp:Date.now()+QUOTE_TTL,impact:imp,
lossTok,lossSym:isIn?t.sym:f.sym,lossDec:isIn?t.dec:f.dec,
fills:r.fills||null,tokenIn:f.addr,tokenOut:t.addr,
source:r.best.source,
wrap:r.wrap||0n,pools:r.pools||null,
mkt,got:isIn?finalOut:finalIn,isIn};
quoting=false;
render();
}catch(e){if(my===seq){
quoting=false;
const transport=!!e?.noWallet||mcGaveUp>gaveUp0||(mcFailed>failed0&&mcOk===ok0);
if(!transport&&!e?.exactOut&&advancePastDeadPair()){loadChart();update();return}
outEl.value="";fitFont(outEl);rankBox.textContent="";rankBox.classList.add("hide");render();
stat.textContent=e?.noWallet?NO_WALLET
:e&&e.exactOut?e.message
:transport
?"Your RPC could not complete the quote — some venues were never asked. Try again, or switch networks in your wallet and back."
:"No route: "+(e.message||e)}}
}
let debounceT=0;
const showNet=()=>{
const e=document.getElementById("net");
if(!e)return;
const c=chainCfg();
e.innerHTML=(c.icon||"")+"<span>"+c.short+"</span>";
e.title="Network: "+c.name+" — click to switch";
};
const switchNet=async()=>{
const ids=Object.keys(CHAINS).map(Number);
const next=ids[(ids.indexOf(CHAIN_ID)+1)%ids.length];
try{
await rpc("wallet_switchEthereumChain",[{chainId:"0x"+next.toString(16)}]);
}catch(e){
if(e&&e.code===4902){
const c=CHAINS[next];
try{await rpc("wallet_addEthereumChain",[{chainId:"0x"+next.toString(16),chainName:c.name,
nativeCurrency:{name:"Ether",symbol:"ETH",decimals:18},rpcUrls:c.rpcs,blockExplorerUrls:[c.explorer]}])}catch{return}
}else return;
}
setChain(next);showNet();location.reload();
};
const DEEPSTATE="0x6cf19308c22fc82ea620fa0b3e94948d20f27b96";
const SEL_POOLID="1338aa1d",SEL_POOLEPOCH="8fa79763",SEL_SWAPDEEP="ad33b1d0";
const OZ20="52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00";
const DEEP_TAKER="0x00000000000000000000000000000000deeb0000";
const MAXU=(1n<<256n)-1n;
const slotKey=(k,sl)=>strip0x(keccak(hexToBytes(k+sl)));
const deepSlots={};
const findSlot=async(tok,sel,keyw,probe)=>{
const ck=tok+sel;
if(deepSlots[ck]!==undefined)return deepSlots[ck];
const cands=[OZ20,(BigInt("0x"+OZ20)+1n).toString(16).padStart(64,"0")];
for(let i=0;i<10;i++)cands.push(encUint(i));
for(const c of cands){
const sl=keyw.length>64?slotKey(keyw.slice(64),slotKey(keyw.slice(0,64),c)):slotKey(keyw,c);
try{
const got=await rpc(C,[{to:tok,data:"0x"+sel+keyw},L,{[tok]:{stateDiff:{["0x"+sl]:"0x"+encUint(probe)}}}]);
if(BigInt(got||0)===probe){deepSlots[ck]=c;return c}
}catch{return null}
}
deepSlots[ck]=null;return null;
};
const deepQuote=async(fa,ta,amt,expOut)=>{
if(CHAIN_ID!==4663||!amt||fa===ZERO||ta===ZERO)return null;
const [t0,t1]=fa.toLowerCase()<ta.toLowerCase()?[fa,ta]:[ta,fa];
const isBid=ta.toLowerCase()===t0.toLowerCase();
try{
const pid=await rpc(C,[{to:DEEPSTATE,data:"0x"+SEL_POOLID+encAddr(t0)+encAddr(t1)},L]);
if(!pid||pid==="0x")return null;
const ep=await rpc(C,[{to:DEEPSTATE,data:"0x"+SEL_POOLEPOCH+strip0x(pid)},L]);
const epoch=BigInt(ep||0);
const bs=await findSlot(fa,"70a08231",encAddr(DEEP_TAKER),0x1234567n);
if(bs===null)return null;
const as=await findSlot(fa,"dd62ed3e",encAddr(DEEP_TAKER)+encAddr(ZROUTER),0x7654321n);
if(as===null)return null;
const ov={[fa]:{stateDiff:{
["0x"+slotKey(encAddr(DEEP_TAKER),bs)]:"0x"+encUint(amt),
["0x"+slotKey(encAddr(ZROUTER),slotKey(encAddr(DEEP_TAKER),as))]:"0x"+encUint(MAXU)}}};
const price=isBid?0x7fffffffn:0x80000000n;
const seed=isBid?(expOut||0n):amt;
if(seed<=0n)return null;
let best=null;
for(let q=seed*4n,n=0;n<5&&q>0n;q/=4n,n++){
const order="0x"+encUint((price<<224n)|((q&((1n<<160n)-1n))<<64n));
const data="0x"+SEL_SWAPDEEP+encAddr(DEEP_TAKER)+encAddr(t0)+encAddr(t1)+encUint(epoch)
+strip0x(order)+encUint(isBid?1:0)+encUint(amt)+encUint(0n)+encUint(MAXU);
try{
const r=await rpc(C,[{from:DEEP_TAKER,to:ZROUTER,data},L,ov]);
const b=strip0x(r||"");
if(b.length<128)continue;
const ai=BigInt("0x"+b.slice(0,64)),ao=BigInt("0x"+b.slice(64,128));
if(ao>0n&&(!best||ao>best.out))best={inUsed:ai,out:ao,t0,t1,isBid,epoch,order};
}catch{}
}
return best;
}catch{return null}
};
const showAddr=async()=>{const a=account,short=a.slice(0,6)+"..."+a.slice(-4);
addr.textContent=short;addr.title=a+" - click to disconnect";addr.classList.add("on");
addr.setAttribute("role","button");addr.tabIndex=0;
const n=await nameRev(a);if(n&&a===account)addr.textContent=n};
async function disconnect(){
sessionStorage.setItem("dc","1");
try{delete LS[WK]}catch{}
location.reload();
}
addr.onclick=()=>{account?disconnect():connect()}
net.onclick=()=>{switchNet()}
net.onkeydown=e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();switchNet()}}
showNet();;
addr.onkeydown=e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();account?disconnect():connect()}};
const AUTO_Q_MAX=5;
const updateSoon=()=>{clearTimeout(debounceT);debounceT=setTimeout(()=>{debounceT=0;update()},250)};
const setMode=m=>{++seq;quoting=false;mode=m;const o=m==="in"?outAmt:amt;o.value="";fitFont(o);last=null;sendReady=null;render()};
amt.addEventListener("input",()=>{
if(lqMode){fitFont(amt);lqPreviewSoon();return}
if(tab==="book"){fitFont(amt);render();return}setMode("in");fitFont(amt);updateSoon()});
outAmt.addEventListener("input",()=>{
if(lqMode){fitFont(outAmt);lqPreviewSoon();return}
if(tab==="book"){fitFont(outAmt);seedFloor();render();return}setMode("out");fitFont(outAmt);updateSoon()});
const slipWarn=()=>slip.classList.toggle("warn",+slip.value>=3);
if(LS.slip&&+LS.slip>=.01&&+LS.slip<=10)slip.value=LS.slip;
slipWarn();
slip.addEventListener("input",()=>{last=null;slipWarn();render();updateSoon()});
rc.addEventListener("input",()=>{
if(lnMode)return;
if(lqMode){clearTimeout(lqToT);lqToT=setTimeout(lqShowTo,300);return}
last=null;sendReady=null;render();updateSoon()});
let lqToT;
const onEnter=e=>{
if(e.key!=="Enter")return;
const go=lnMode?lnGo:swap;
if(!go||go.classList.contains("hide"))return;
e.preventDefault();
if(debounceT){clearTimeout(debounceT);debounceT=0;update();return}
if(!go.disabled)go.click();
};
for(const el of[amt,outAmt,rc,slip,floorAmt,nftId,lnName,lnSym,lnSupply,lnMcap,lnAlloc,lnGoal,lnDays])
if(el)el.addEventListener("keydown",onEnter);
slip.addEventListener("change",()=>{slip.value=Math.min(10,Math.max(.01,+slip.value||.5));slipWarn();LS.slip=slip.value});
for(const sel of [fromSel,toSel]){
sel.dataset.prev=sel.value;
sel.addEventListener("change",async()=>{
if(sel.value!=="__custom"){
sel.dataset.prev=sel.value;
sel.dataset.addr=TOKENS[sel.value]?.addr||"";
pairChosen=true;
syncDisabled(sel);
syncNftUI();
if(sel===fromSel)refreshBalance();else refreshBalance1();
loadChart();
if(tab==="book"&&bookRows.length)paintBook();
update();
return;
}
last=null;render();
const prev=sel.dataset.prev;
let a;
try{a=(prompt("Paste token address:")||"").trim()}
catch{sel.value=prev;syncDisabled();
stat.textContent="This browser blocked the address prompt — add the token with a #token=0x… link instead.";
return}
if(!a){sel.value=prev;update();return}
stat.textContent="Loading token...";
try{
const idx=await addCustomToken(a);
rebuild();
const otherSel=sel===fromSel?toSel:fromSel;
if(String(idx)===otherSel.value){otherSel.value=prev;otherSel.dataset.prev=prev}
sel.value=String(idx);
sel.dataset.prev=sel.value;
pairChosen=true;
stat.textContent="";
syncDisabled(sel);syncNftUI();
refreshBalance();
update();
}catch(e){
const msg="Couldn't load token: "+(e.message||e);
sel.value=prev;
syncDisabled();syncNftUI();refreshBalance();update();
stat.textContent=msg;
}
});
}
flip.onclick=()=>{pairChosen=true;const a=fromSel.value,b=toSel.value,v=outAmt.value.replace(/,/g,"");fromSel.value=b;toSel.value=a;
amt.value=(v&&!isNaN(+v))?v:"";setMode("in");fitFont(amt);syncDisabled();syncNftUI();refreshBalance();loadChart();update()};
const sameAcct=(a,b)=>!!a&&!!b&&a.toLowerCase()===b.toLowerCase();
const wkBind=eth=>{
if(!eth||eth.__zb)return;
eth.__zb=1;
eth.on?.("chainChanged",cid=>{if(connecting)return;if(CHAINS[+cid]&&account){setChain(+cid);location.reload();return}location.reload()});
eth.on?.("accountsChanged",accs=>{if(connecting||sameAcct(accs?.[0],account))return;location.reload()});
};
const wireWallet=eth=>{
if(!eth||wireWallet.done)return;
wireWallet.done=true;
wkBind(eth);
(async()=>{
if(sessionStorage.getItem("dc"))return;
try{
const accs=await rpc("eth_accounts",[]);
const cid=await rpc(I,[]);
wireWallet.probed=1;
const hop=!!CHAINS[+cid]&&+cid!==CHAIN_ID;
if(hop){setChain(+cid);showNet()}
if(accs[0]&&CHAINS[+cid]){account=accs[0];showAddr()}
if(account||hop){
const keep2=pairAddrs();
await loadTokenList();
adoptList(keep2);
if(hop&&location.hash.length>1)applyLink();
refreshBalance();update();if(account)loadPos();syncChart();
}
}catch{}
})();
(async()=>{
const self=selfFromUrl()||ZSWAP_SELF;
if(!self)return;
try{
if(+(await rpc(I,[]))!==CHAIN_ID)return;
const ret=strip0x(await rpc(C,[{to:self,data:"0x"+SEL_LATEST},L])||"");
if(ret.length!==64)return;
let tip="0x"+ret.slice(24);
const me=self.toLowerCase();
if(BigInt(tip)===0n||tip===me)return;
const rd=async(to,sel)=>{const h=strip0x(await rpc(C,[{to,data:"0x"+sel},L])||"");return h.length===64?h:""};
const now=Date.now()/1000;
for(let i=0;i<32&&tip!==me;i++){
const pv=await rd(tip,SEL_PREV);
if(!pv)break;
const prev="0x"+pv.slice(24);
if(BigInt(prev)===0n)break;
const at=await rd(prev,SEL_SUCCAT);
const born=at?Number(BigInt("0x"+at)):0;
if(!born||now>=born+MATURITY)break;
tip=prev;
}
if(tip===me)return;
const h=(location.hostname||"").split(".");
const url=/^0x[0-9a-fA-F]{40}$/.test(h[0]||"")
?`${location.protocol}//${[tip,...h.slice(1)].join(".")}/`
:`https://${tip}.w4eth.io/`;
footAddr.innerHTML+=` · <a href="${url}" target="_blank" rel="noopener" `
+`title="A newer version exists on chain. This page is unchanged and always will be; the link opens the successor.">newer →</a>`;
}catch{}
})();
};
const wkGo=()=>{wireWallet(prov());
if(!prov())window.addEventListener("ethereum#initialized",()=>wireWallet(prov()),{once:true})};
if(!PROV&&LS[WK]&&LS[WK]!=="wc")wkRestore().then(wkGo,wkGo);else wkGo();
setTimeout(burnTotal,0);
setTimeout(loadEthUsd,0);
const SEL_TRANSFER="a9059cbb";
let SB2=MB.sb;
let SB1=MB.s1;
const SBVIEW="0x000000E0b25449F32f7D9259aC449bA88E78dFCE";
let SWAPBOL=MB.sw;
let DUTCH=MB.du;
let ORDERBOL=MB.ob;
let FLOOR=MB.fl;
const FLOORVIEW="0x0000004E376e9dB5D9EC28E6711E1a64997C6ba7";
const SEL_NEXTID="2a58b330",SEL_NEXTID2="61b8ce8c",SEL_GETORDERS="03652027",SEL_CANCELORD="514fcac7",SEL_EXPIRED="d6c3de07",
SEL_WITHDRAW="ef3d3521",SEL_REPLACE="84343d3a";
const SEL_FILL1="c37dfc5b",SEL_FILL2="9d136c7f",SEL_FILL2_UNWRAP="3987baf4",SEL_FILL2_ETH="6f608bab";
const SEL_RECENT="6a9849c1",SEL_RECENT_DUTCH="98035c9a",SEL_DUTCH_FILL="ae7a8260",SEL_DUTCH_CANCEL="40e58ee5";
const SEL_CANCEL_UNWRAP="21dd76f9",SEL_DUTCH_CANCEL_UNWRAP="8382de65";
const SEL_DUTCH_LISTING="de74e57b";
const SEL_CANDS="5f452988",SEL_DUTCH_CANDS="eb33e466",SEL_FILLPLAN="c277f67c";
const SEL_FILLPLAN_SWAP="9090c8e5",SEL_SNWAP="5f3bd1c8";
const SEL_COLL_BIDS="16bb24eb";
const SEL_FLOOR_CANDS="c587d927",SEL_RECENT_FLOOR="a5edd13d";
const SEL_ORDER_FLOOR="23e93357";
const SEL_BOL_FLOOR="b732d224";
let floorReady=null;
async function floorRoutable(block){
if(FLOOR===ZERO||FLOORVIEW===ZERO)return false;
if(floorReady!==null)return floorReady;
try{
const h=await rpc(C,[{to:SWAPBOL,data:"0x"+SEL_BOL_FLOOR},block||L]);
floorReady=("0x"+strip0x(h).slice(-40)).toLowerCase()===FLOOR.toLowerCase();
}catch{return false}
return floorReady;
}
const SEL_BIDS="4423c5f1";
const SEL_INITIAL_B="878ea250",SEL_TOKENURI="c87b56dd",SEL_LOGOOF="6ce273ac";
const SEL_QUOTEFILL="f325beda";
async function preflightLeg(row,routeIn,routeOut,pay){
if(!row.floor)return preflightAsk(row,pay);
if(!await floorRoutable(L))throw Error("this executor cannot route bids yet");
const want=routeIn===ZERO?WETH:routeIn,gets=routeOut===ZERO?WETH:routeOut;
let h;
try{h=await rpc(C,[{to:FLOOR,data:"0x"+SEL_BIDS+encUint(row.id)},L])}
catch{throw Error("bid could not be read — refresh and retry")}
const at=i=>strip0x(h).slice(i*64,(i+1)*64);
if(strip0x(h).length<11*64)throw Error("bid could not be read — refresh and retry");
const addr=i=>"0x"+at(i).slice(24);
if(/^0x0{40}$/i.test(addr(0)))throw Error("bid is closed — refresh and retry");
if(BigInt("0x"+at(1)))throw Error("NFT bids are not routable from here");
if(addr(4).toLowerCase()!==want.toLowerCase()
||addr(6).toLowerCase()!==gets.toLowerCase())
throw Error("bid changed — refresh and retry");
if(BigInt("0x"+at(10))<pay)throw Error("bid partly taken — refresh and retry");
}
async function preflightFills(fills,tokenIn,tokenOut){
if(!fills||!fills.length)return;
const inA=(tokenIn===ZERO?WETH:tokenIn).toLowerCase();
const outA=(tokenOut===ZERO?WETH:tokenOut).toLowerCase();
const reads=fills.map(f=>f.board.toLowerCase()===FLOOR.toLowerCase()
?{to:FLOOR,data:"0x"+SEL_BIDS+encUint(f.id)}
:f.board.toLowerCase()===DUTCH.toLowerCase()
?{to:DUTCH,data:"0x"+SEL_DUTCH_LISTING+encUint(f.id)}
:{to:f.board,data:"0x"+SEL_GETORDERS+encUint(32)+encUint(1)+encUint(f.id)});
const res=await mc3Deep(reads);
const stale=()=>{throw Error("an order in this route just changed — refreshing")};
for(let i=0;i<fills.length;i++){
const f=fills[i],raw=res[i];
if(!raw)stale();
const s=strip0x(raw),at=k=>BigInt("0x"+s.slice(k*64,(k+1)*64));
if(f.board.toLowerCase()===FLOOR.toLowerCase()){
if(s.length<11*64)stale();
const ad=k=>"0x"+s.slice(k*64+24,(k+1)*64);
if(/^0x0{40}$/i.test(ad(0))||at(1))stale();
if(ad(4).toLowerCase()!==inA||ad(6).toLowerCase()!==outA)stale();
if(at(10)<f.pay)stale();
}else if(f.board.toLowerCase()===DUTCH.toLowerCase()){
if(s.length<11*64||at(0)===0n)stale();
if(at(9)<f.get)stale();
if(at(10)&&BigInt(Math.floor(Date.now()/1e3))>at(10))stale();
}else{
const wds=WORDS(f.board.toLowerCase()===SB2.toLowerCase()?1:0);
if(s.length<(2+wds)*64)stale();
const w=k=>at(2+k),ad=k=>"0x"+s.slice((2+k)*64+24,(3+k)*64);
if(w(1)!==1n)stale();
const v2=wds===11,iA=v2?7:2,iB=v2?9:4;
if(ad(iA).toLowerCase()!==outA||ad(iB).toLowerCase()!==inA)stale();
if(w(iB+1)<f.pay)stale();
if(v2&&w(iA+1)<f.get)stale();
if(v2&&w(3)&&BigInt(Math.floor(Date.now()/1e3))>w(3))stale();
}
}
}
async function preflightAsk(row,pay){
if(row.dutch){
let h;
try{h=await rpc(C,[{to:DUTCH,data:"0x"+SEL_DUTCH_LISTING+encUint(row.id)},L])}
catch{throw Error("listing could not be read — refresh and retry")}
const s=strip0x(h);
if(s.length<640)throw Error("listing could not be read — refresh and retry");
if(BigInt("0x"+s.slice(0,64))===0n)throw Error("listing is closed — refresh and retry");
return;
}
let h;
try{h=await rpc(C,[{to:row.board,
data:"0x"+SEL_GETORDERS+encUint(32)+encUint(1)+encUint(row.id)},L])}
catch{throw Error("order could not be read — refresh and retry")}
const s=strip0x(h),wds=WORDS(row.v2);
if(s.length<(2+wds)*64)throw Error("order could not be read — refresh and retry");
const at=k=>BigInt("0x"+s.slice(128+k*64,128+(k+1)*64));
const ad=k=>"0x"+s.slice(128+k*64+24,128+(k+1)*64);
if(at(1)!==1n)throw Error("order is no longer active — refresh and retry");
const iA=row.v2?7:2,iB=row.v2?9:4;
if(ad(iA).toLowerCase()!==row.tA.toLowerCase()
||ad(iB).toLowerCase()!==row.tB.toLowerCase())
throw Error("order changed — refresh and retry");
if(at(iA+1)>row.aA||at(iB+1)>row.aB)throw Error("order changed — refresh and retry");
if(pay!==undefined&&at(iB+1)<pay)throw Error("order partly taken — refresh and retry");
}
const SEL_ORDER_FIXED="bcdb7936",SEL_ORDER_DUTCH="bbd94e1a",SEL_CHECKPOINT="a972985e";
const BOARDS=()=>[{a:SB2,v2:1},{a:SB1,v2:0}].filter(b=>b.a!==ZERO);
const WORDS=b=>b?11:6;
let bookSeq=0,bookRows=[],candCache={},bkOpen=LS.bk!=="0",bookTrunc=false;
let busy=0;
if(LS.wm==="weth"||LS.wm==="auto")ethMode.value=LS.wm;
ethMode.onchange=()=>{LS.wm=ethMode.value};
const wantsUnwrap=()=>ethMode.value!=="weth";
const planWethPayment=async need=>{
let w=0n,e=0n;
try{
const[wh,eh]=await Promise.all([
rpc(C,[{to:WETH,data:"0x"+SEL_BALANCEOF+encAddr(account)},L]),
rpc("eth_getBalance",[account,L])]);
w=BigInt(wh);e=BigInt(eh);
}catch{return{rail:"weth",wrap:0n,w,e}}
if(w>=need)return{rail:"weth",wrap:0n,w,e};
if(e>=need)return{rail:"eth",wrap:0n,w,e};
let gas=0n;
try{gas=BigInt(await rpc("eth_gasPrice",[]))*400000n}catch{}
const spendable=e>gas?e-gas:0n;
if(w+spendable>=need)return{rail:"weth",wrap:need-w,w,e};
return{rail:"short",wrap:0n,w,e};
};
const assertFunded=async(token,need,meta)=>{
if(!need)return;
let have;
try{
have=token===ZERO
?BigInt(await rpc("eth_getBalance",[account,L]))
:BigInt(await rpc(C,[{to:token,data:"0x"+SEL_BALANCEOF+encAddr(account)},L]));
}catch{return}
if(have>=need)return;
const m=meta||known(token)||{sym:token===ZERO?"ETH":"tokens",dec:18};
throw Error(`Not enough ${m.sym} to fill: it costs ${trimAmt(need,m.dec)} `
+`and you hold ${trimAmt(have,m.dec)}.`);
};
const known=a=>TOKENS.find(t=>t.addr.toLowerCase()===a.toLowerCase());
const sm=i=>i.replace('width="20" height="20"','width="15" height="15"');
const escan=(a,id)=>!isAddr(a)?"":`<a href="${chainCfg().explorer}/${id!=null?`nft/${a}/${id}`:`token/${a}`}" target="_blank" rel="noreferrer">${a.slice(0,6)}…${a.slice(-4)} ↗</a>`;
const SEL_CONTRACTURI="e8a3d485";
const okDataImg=u=>typeof u==="string"&&u.length<=60000
&&/^data:image\/[a-z.+-]{2,20};base64,[A-Za-z0-9+/=]+$/i.test(u);
const imgFromContractURI=raw=>{
const P="data:application/json;base64,";
const u=String(raw||"").trim();
if(!u.startsWith(P)||u.length>400000)return "";
let txt;
try{
const bin=atob(u.slice(P.length));
txt=new TextDecoder().decode(Uint8Array.from(bin,c=>c.charCodeAt(0)));
}catch{return ""}
let j;try{j=JSON.parse(txt)}catch{return ""}
return okDataImg(j&&j.image)?j.image:"";
};
const capObj=(o,max)=>{const ks=Object.keys(o);
for(let i=0;i<ks.length-max;i++)delete o[ks[i]]};
const capMap=(m,max)=>{while(m.size>max)m.delete(m.keys().next().value)};
const LOGO_MAX=192,CARD_MAX=64,META_MAX=256;
const logoCache={};
async function loadLogos(addrs){
const want=[...new Set(addrs.map(a=>a.toLowerCase()))]
.filter(a=>a!==ZERO&&!known(a)&&logoCache[a]===undefined);
if(!want.length)return;
let raw=[];
if(TOKENLIST!==ZERO)raw=await cfgBatch(want.map(a=>({to:TOKENLIST,data:"0x"+SEL_LOGOOF+encAddr(a)}))).catch(()=>[]);
want.forEach((a,i)=>{let u="";try{if(raw[i])u=safeDataUrl(decodeString(raw[i]))}catch{}logoCache[a]=u});
capObj(logoCache,LOGO_MAX);
const self=want.filter(a=>!logoCache[a]);
if(!self.length)return;
const cu=await mc3Deep(self.map(a=>({to:a,data:"0x"+SEL_CONTRACTURI}))).catch(()=>[]);
self.forEach((a,i)=>{try{
const u=imgFromContractURI(cu[i]?decodeString(cu[i]):"");
if(u)logoCache[a]=u;
}catch{}});
}
async function leg(addr,amt,isNft,meta){
const k=known(addr);
if(isNft)return{ic:genIcon("#"),txt:`#${amt}`,ok:0,link:escan(addr,amt)};
const raw=meta&&meta.sym?meta:await tokMeta(addr),m={...raw,sym:safeSym(raw.sym)};
const lg=k?"":logoCache[addr.toLowerCase()];
const ic=k?sm(k.icon):lg?`<img src="${lg}" width="15" height="15">`:genIcon(m.sym);
return{ic,txt:`${trimAmt(amt,m.dec)} ${k?safeSym(k.sym):'“'+m.sym+'”'}`,ok:!!k,
sym:k?safeSym(k.sym):m.sym,dec:m.dec,link:addr===ZERO?"ETH":escan(addr)};
}
let floorSeq=0;
nftId.addEventListener("input",()=>{syncOrderType();showFloor();render()});
async function showFloor(){
const el=document.getElementById("floorNote");if(!el)return;
const side=isNft(fromSel.value)?fromSel.value:isNft(toSel.value)?toSel.value:null;
const live=tab==="book"&&side!==null&&!nftId.value.trim();
if(!live){el.classList.add("hide");el.textContent="";return}
const seq=++floorSeq,t=TOKENS[side];
el.classList.remove("hide");el.textContent="Checking floor bids…";
const oth=isNft(fromSel.value)?toSel.value:fromSel.value;
const qt=isNft(oth)?ZERO:(TOKENS[oth].addr===ZERO?WETH:TOKENS[oth].addr);
const rows=await collectionFloor(t.addr,qt).catch(()=>[]);
if(seq!==floorSeq)return;
if(!rows.length){el.textContent="No standing bids on this collection.";return}
const best=rows[0],q=known(best.quote);
el.textContent=`${rows.length} collection bid${rows.length>1?"s":""} · best pays `
+`${trimAmt(best.price/best.initial,q?q.dec:best.qDec??18)} ${q?q.sym:safeSym(best.qSym||"?")} for any ${t.sym}`;
}
function decBidRows(hex){
const out=[];
try{
const h=strip0x(hex),bytes=h.length/2;
const word=b=>{if(b<0||b+32>bytes)throw 0;return BigInt("0x"+h.slice(b*2,(b+32)*2))};
const num=b=>{const v=word(b);if(v>0xffffffffffffffffn)throw 0;return Number(v)};
const arr=num(0);
if(arr<64||arr+32>bytes)throw 0;
const n=num(arr),base=arr+32;
if(n>256||base+n*32>bytes)throw 0;
for(let i=0;i<n;i++){
try{
const row=base+num(base+i*32);
if(row+17*32>bytes)throw 0;
const f=k=>word(row+k*32);
const txt=k=>{const p=row+num(row+k*32),len=num(p);
if(p+32+len>bytes)throw 0;
return safeSym(new TextDecoder().decode(hexToBytes(h.slice((p+32)*2,(p+32+Math.min(len,1024))*2))))};
const dec=k=>{const v=num(row+k*32);return v>0&&v<=37?v-1:null};
out.push({
id:f(0),
bidder:"0x"+h.slice((row+1*32+12)*2,(row+2*32)*2),
to