0xacfba7ce…4082·#25,751,347·view on Etherscan
+ps,4,8,1,SS)]);
}
const tailStep=swapAmt/100n||1n,tailAmt=swapAmt>tailStep?swapAmt-tailStep:0n;
const tailJobs=[];
if(tailAmt){
const z=encBool(!isIn)+encAddr(f.addr)+encAddr(t.addr)+encUint(tailAmt)+encUint(0n)+encUint(deadline);
tailJobs.push(Q("e453166e",a+rf+z,4,9),Q("4c464f59",a+z,8,13));
if(isIn)for(const x of["892af013","85f86a90"]){
const ps=encAddr(f.addr)+encAddr(t.addr)+encUint(tailAmt)+encUint(splitSlip)+encUint(deadline);
tailJobs.push(Q(x,a+ps,4,8,1,splitSlip));
}
}
const refAmt=swapAmt/REF_N;
const iRef=refAmt>0n
?Q("e453166e",a+rf+encBool(!isIn)+encAddr(f.addr)+encAddr(t.addr)+encUint(refAmt)+encUint(0n)+encUint(deadline),4,9)
:-1;
const mcRaw=await mc3Deep(_rq.map(q=>({to:q.to,data:q.data})),quoteBlock);
if(my!==seq)return;
const dec=_rq.map((q,i)=>{try{return mcRaw[i]?decQ(mcRaw[i],q.sb||slipBps,!isIn,q.u,q.v,q.S,q.mv):null}catch{return null}});
for(const g of jobs)pick(g.length>1?merge(dec[g[0]],dec[g[1]]):dec[g[0]]);
if(isIn&&!(dec[jobs[1][0]]||dec[jobs[1][1]]||dec[jobs[2][0]]||dec[jobs[2][1]])){
const hq=await hubRoute(f,t,swapAmt,slipBps,deadline,rcv||acct||ZERO,acct||ZERO,quoteBlock).catch(()=>null);
if(my!==seq)return;
if(hq)pick(hq);
}
let tail=null;
for(const i of tailJobs){const y=dec[i];if(!y)continue;
const v=isIn?y.best.amountOut:y.best.amountIn;
if(v&&(!tail||(isIn?v>tail.best.amountOut:v<tail.best.amountIn)))tail=y;
}
if(my!==seq)return;
const v4q=await v4Job;
let standalone=null;
let v4c=null;
if(v4q&&v4q.amountOut>0n){
const v4Min=v4q.amountOut-(v4q.amountOut*slipBps)/10000n;
const p=v4q.pool;
v4c={best:{source:4,amountIn:v4q.amountIn,amountOut:v4q.amountOut,feeBps:BigInt(p.fee)/100n},
callData:"0x"+SEL_V4SWAP+encPoolKey(p)+encBool(p.zeroForOne)+encUint(v4q.amountIn)
+encUint(v4Min)+encAddr(rcv||acct||ZERO)+encUint(deadline),
fundedCallData:null,amountLimit:v4Min,msgValue:f.addr===ZERO?swapAmt:0n,
to:V4PORT,spender:f.addr===ZERO?null:V4PORT};
if(!r){r=v4c;standalone=v4c}
}
const pq=await preJob;
if(my!==seq)return;
let pc=null;
if(pq&&pq.out>0n){
const pMin=pq.out-(pq.out*slipBps)/10000n;
const pNative=pq.tokenIn===ZERO;
const pWethIn=pNative&&(pq.unwrap||0n)>0n;
const pTo=rcv||acct||ZERO;
const pFund=pWethIn?WETH:pq.tokenIn;
const routeData=pWethIn
?encPRouteWeth([pq.pool],pq.tokenOut,swapAmt,pMin,pTo)
:encPRoute([pq.pool],pq.tokenIn,pq.tokenOut,swapAmt,pMin,pTo);
const routedLeg=encSnwap(pFund,swapAmt,pTo,pq.tokenOut,pMin,PROUTE,routeData);
const checkpointLeg=encSnwap(ZERO,0n,acct||ZERO,ZERO,0n,PROUTE,
"0x"+SEL_PCHECKPOINT+encAddr(pFund)+strip0x(keccak(hexToBytes(routeData)))+encAddr(acct||ZERO));
const pCall=(pNative&&!pWethIn)?routedLeg:encCalls([checkpointLeg,routedLeg]);
const pFunded=(pNative&&!pWethIn)?null:encCalls([
checkpointLeg,
encSweep(pFund,swapAmt,PROUTE),
encSnwap(ZERO,0n,pTo,pq.tokenOut,pMin,PROUTE,routeData),
]);
pc={best:{source:SRC_PRECISION,amountIn:swapAmt,amountOut:pq.out,feeBps:pq.fee/100n},
callData:pCall,
fundedCallData:pFunded,amountLimit:pMin,msgValue:(pNative&&!pWethIn)?swapAmt:0n,
wrap:pq.wrap||0n,outDiffers:!!pq.outDiffers,poolOut:pq.tokenOut,
to:null,spender:(pNative&&!pWethIn)?null:ZROUTER,pool:pq.pool};
if(!r){r=pc;standalone=pc}
}
if(!r&&curveOnlyOut)throw Error(`exact-output is unavailable for this pair — enter an amount to PAY instead`);
if(!r)throw Error("bad quote");
if(r.best.amountIn<=0n||r.best.amountOut<=0n||(!isIn&&r.amountLimit<r.best.amountIn))throw Error("bad quote");
let hybrid=null,bookIncomplete=false,bookUnavailable=false,plannerBounded=false;
if(acct&&!standalone){
const candidates=await candJob;
bookIncomplete=!!candidates.incomplete;
bookUnavailable=!!candidates.unavailable;
if(my!==seq)return;
const quoteRem=async(x,eo)=>{
const qs=[],groups=[],add=(sel,data,u,v,S,sb)=>{
qs.push({data:"0x"+sel+data,u,v,S,sb});return qs.length-1};
const z0=encBool(eo)+encAddr(f.addr)+encAddr(t.addr)+encUint(x)+encUint(0n)+encUint(deadline);
const zs=encBool(eo)+encAddr(f.addr)+encAddr(t.addr)+encUint(x)+encUint(slipBps)+encUint(deadline);
groups.push([add("e453166e",a+rf+z0,4,9),add("e453166e",a+rf+zs,4,9)]);
groups.push([add("4c464f59",a+z0,8,13),add("4c464f59",a+zs,8,13)]);
if(!eo){
const w=slipBps*3n,ss=w<150n?150n:w>500n?500n:w;
const ps=encAddr(f.addr)+encAddr(t.addr)+encUint(x)+encUint(ss)+encUint(deadline);
for(const sel of["892af013","85f86a90"])groups.push([add(sel,a+ps,4,8,1,ss)]);
}
const raw=await mc3Deep(qs.map(q=>({to:ZQUOTER,data:q.data})),quoteBlock);
const ds=qs.map((q,i)=>{try{return raw[i]?decQ(raw[i],q.sb||slipBps,eo,q.u,q.v,q.S):null}catch{return null}});
let best=null;
for(const g of groups){
const y=g.length>1?merge(ds[g[0]],ds[g[1]]):ds[g[0]];
if(!y)continue;
if(eo&&y.sources&&y.sources.includes(SRC_CURVE))continue;
const v=eo?y.best.amountIn:y.best.amountOut;
if(v&&(!best||(eo?v<best.best.amountIn:v>best.best.amountOut)))best=y;
}
return best;
};
const gasValue=async wei=>{
const token=isIn?t.addr:f.addr;
if(token===ZERO||token.toLowerCase()===WETH.toLowerCase())return wei;
const eo=!isIn,ti=isIn?ZERO:token,to=isIn?token:ZERO;
const z=encBool(eo)+encAddr(ti)+encAddr(to)+encUint(wei)+encUint(0n)+encUint(deadline);
try{
const raw=await rpc(C,[{to:ZQUOTER,data:"0xe453166e"+a+rf+z},quoteBlock]);
const q=decQ(raw,0n,eo,4,9);
return isIn?q.best.amountOut:q.best.amountIn;
}catch{return null}
};
const tailDelta=swapAmt-tailAmt;
const seedOut=isIn&&tail&&r.best.amountOut>tail.best.amountOut
?r.best.amountOut-tail.best.amountOut:r.best.amountOut;
const seedIn=!isIn&&tail&&r.best.amountIn>tail.best.amountIn
?r.best.amountIn-tail.best.amountIn:r.best.amountIn;
let p=isIn
?planBookExactIn(candidates,swapAmt,seedOut,tail&&seedOut!==r.best.amountOut?tailDelta:r.best.amountIn)
:planBookExactOut(candidates,swapAmt,seedIn,tail&&seedIn!==r.best.amountIn?tailDelta:r.best.amountOut);
plannerBounded=!!(p&&p.bounded);
let amm=null;
for(let round=0;p&&(isIn?p.ammIn:p.ammOut)&&round<2;round++){
const rem=isIn?p.ammIn:p.ammOut;
amm=await quoteRem(rem,!isIn).catch(()=>null);
if(!amm)break;
const next=isIn
?planBookExactIn(candidates,swapAmt,amm.best.amountOut,rem)
:planBookExactOut(candidates,swapAmt,amm.best.amountIn,rem);
if(!next){p=null;break}
const stable=(isIn?next.ammIn===p.ammIn:next.ammOut===p.ammOut);
p=next;plannerBounded=plannerBounded||!!p.bounded;if(stable)break;
}
if(p){
const rem=isIn?p.ammIn:p.ammOut;
amm=rem?await quoteRem(rem,!isIn).catch(()=>null):null;
if(!rem||amm){
const recipient=rcv||acct;
const ammBudget=amm?(isIn?rem:amm.amountLimit):0n;
const totalBudget=p.bookIn+ammBudget;
const expectedIn=p.bookIn+(amm?amm.best.amountIn:0n);
const expectedOut=p.bookOut+(amm?amm.best.amountOut:0n);
const saving=isIn
?(expectedOut>r.best.amountOut?expectedOut-r.best.amountOut:0n)
:(expectedIn<r.best.amountIn?r.best.amountIn-expectedIn:0n);
let gasFloor=null;
if(saving){
const gasPrice=await gasPriceJob;
if(gasPrice){
const extraWei=gasPrice*(120000n+105000n*BigInt(p.fills.length));
gasFloor=await gasValue(extraWei);
}
}
const edgeBps=5n+2n*BigInt(p.fills.length);
const fallbackWin=isIn
?expectedOut*10000n>r.best.amountOut*(10000n+edgeBps)
:expectedIn*(10000n+edgeBps)<r.best.amountIn*10000n;
const wins=saving>0n&&(gasFloor===null?fallbackWin:saving*10n>gasFloor*12n);
if(my!==seq)return;
if(wins){
const fp=encFillPlanAndSwap(
p.fills,f.addr,t.addr,recipient,acct,deadline,ammBudget,amm?amm.callData:"0x"
);
const minOut=isIn?p.bookOut+(amm?amm.amountLimit:0n):swapAmt;
const snwapIn=totalBudget===0n?ZERO:f.addr;
const planCall=encSnwap(snwapIn,totalBudget,recipient,t.addr,minOut,SWAPBOL,fp);
const callData=f.addr===ZERO?planCall:encCalls([
encCheckpoint(SWAPBOL,f.addr,acct),planCall
]);
let fundedCallData=null;
if(f.addr!==ZERO){
fundedCallData=encPermit2Hybrid(
amm?amm.callData:"0x",p.fills,f.addr,t.addr,recipient,acct,deadline,p.bookIn,p.bookOut
);
}
r={...r,best:{...r.best,amountIn:expectedIn,amountOut:expectedOut},callData,
fills:p.fills,
fundedCallData,amountLimit:isIn?minOut:totalBudget,msgValue:f.addr===ZERO?totalBudget:0n,
slipBps:amm&&amm.slipBps};
hybrid=isIn
?{bookIn:p.bookIn,bookOut:p.bookOut,ammIn:p.ammIn}
:{bookIn:p.bookIn,bookOut:p.bookOut,ammOut:p.ammOut};
plannerBounded=plannerBounded||!!p.bounded;
}
}
}
}
for(const c of [v4c,pc])
if(c&&c!==r&&c.best.amountOut>r.best.amountOut){r=c;hybrid=null}
const finalIn=r.best.amountIn;
const finalOut=r.best.amountOut;
if(isIn){outAmt.value=trimAmt(finalOut,t.dec);fitFont(outAmt)}
else{amt.value=trimAmt(finalIn,f.dec);fitFont(amt)}
const rx=(+formatUnits(finalOut,t.dec))/(+formatUnits(finalIn,f.dec));
const s=r.best.source,raw=SOURCES[s]||"?";
const src=hybrid?"Orderbook + AMM":(s===3||s===4||s===SRC_PRECISION)&&r.best.feeBps?`${raw}\u00a0${Number(r.best.feeBps)/100}%`:raw;
rate.textContent=isFinite(rx)?`1 ${f.sym} ≈ ${rx>=1?rx.toLocaleString("en-US",{maximumFractionDigits:4}):rx.toPrecision(4)} ${t.sym} · ${src}`:"";
const paysSym=r.poolOut===ZERO?"ETH":"WETH";
rcvHdr.textContent=r.outDiffers?`You receive \u2014 as ${paysSym}`:"You receive";
if(r.outDiffers&&rate.textContent)rate.textContent+=` \u00b7 pays ${paysSym}`;
rate.title="";
if(isIn)rate.textContent+=` · Min ${trimAmt(r.amountLimit,t.dec)} ${t.sym}`;
else{
const exactMax=formatUnits(r.amountLimit,f.dec);
rate.textContent+=` · Max ${maxAmt(r.amountLimit,f.dec)} ${f.sym}`;
rate.title=`Exact maximum: ${exactMax} ${f.sym}`;
}
if(r.slipBps>slipBps)rate.textContent+=` · Split — bound widened to ${Number(r.slipBps)/100}%`;
if(bookIncomplete)rate.textContent+=" · Book scan capped";
if(plannerBounded)rate.textContent+=" · Planner heuristic/capped";
if(bookUnavailable)rate.textContent+=" · AMM only (books offline)";
if(pq&&pq.capped)rate.textContent+=` · Precision scan capped (${LQ_SCAN})`;
const refQ=iRef>=0&&dec[iRef]?dec[iRef].best:null;
const imp=refQ?impactBps(isIn?finalOut:finalIn,isIn?refQ.amountOut:refQ.amountIn,!isIn):null;
const mkt=refQ?(isIn?refQ.amountOut:refQ.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.`;
}
const inApprove=isIn?swapAmt:r.msgValue||r.amountLimit;
if(acct)last={callData:r.callData,fundedCallData:r.fundedCallData,msgValue:r.msgValue,
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,pool:r.pool||null,
mkt,got:isIn?finalOut:finalIn,isIn};
quoting=false;
render();
}catch(e){if(my===seq){
quoting=false;
if(advancePastDeadPair()){loadChart();update();return}
outEl.value="";fitFont(outEl);render();
stat.textContent=e?.noWallet?NO_WALLET
:mcGaveUp>gaveUp0||(mcFailed>failed0&&mcOk===ok0)
?"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 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{await rpc("wallet_revokePermissions",[{eth_accounts:{}}])}catch{}
location.reload();
}
addr.onclick=()=>{if(account)disconnect()};
addr.onkeydown=e=>{if(account&&(e.key==="Enter"||e.key===" ")){e.preventDefault();disconnect()}};
const AUTO_Q_MAX=5;
const updateSoon=()=>{clearTimeout(debounceT);debounceT=setTimeout(()=>{debounceT=0;update()},250)};
const setMode=m=>{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])
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;if(v&&!isNaN(+v))amt.value=v;setMode("in");fitFont(amt);syncDisabled();syncNftUI();refreshBalance();loadChart();update()};
const sameAcct=(a,b)=>!!a&&!!b&&a.toLowerCase()===b.toLowerCase();
const wireWallet=eth=>{
if(!eth||wireWallet.done)return;
wireWallet.done=true;
eth.on?.("chainChanged",cid=>{if(connecting||(+cid===1&&account))return;location.reload()});
eth.on?.("accountsChanged",accs=>{if(connecting||sameAcct(accs?.[0],account))return;location.reload()});
(async()=>{
if(sessionStorage.getItem("dc"))return;
try{
const accs=await rpc("eth_accounts",[]);
const cid=await rpc(I,[]);
if(accs[0]&&+cid===1){
account=accs[0];
showAddr();
const keep2=pairAddrs();
await loadTokenList();
adoptList(keep2);
refreshBalance();update();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<4&&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{}
})();
};
wireWallet(window.ethereum);
if(!window.ethereum)window.addEventListener("ethereum#initialized",()=>wireWallet(window.ethereum),{once:true});
const SEL_TRANSFER="a9059cbb";
const SB2="0x000000dA7bb4B2A9E3e80e9A4D4157E26CA6189b";
const SB1="0x000000fF3D7A2d373615141d7489Ca66683DbecF";
const SBVIEW="0x000000E0b25449F32f7D9259aC449bA88E78dFCE";
const SWAPBOL="0x00000087A6dc5071779Ed1F8274A39230768B976";
const DUTCH="0x000000a213b430D14Bae6062c176289B05e04489";
const ORDERBOL="0x000000e6c7a12C80525ee74e7434aAb919447D95";
const FLOOR="0x00000080198137F790DA4C52bb902cf87c276748";
const FLOORVIEW="0x0000004E376e9dB5D9EC28E6711E1a64997C6ba7";
const SEL_NEXTID="2a58b330",SEL_GETORDERS="03652027",SEL_CANCELORD="514fcac7";
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);
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<640||at(9)===0n)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();
}
}
}
async function preflightAsk(row){
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(9*64,10*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");
}
const SEL_ORDER_FIXED="bcdb7936",SEL_ORDER_DUTCH="bbd94e1a",SEL_CHECKPOINT="a972985e";
const BOARDS=[{a:SB2,v2:1},{a:SB1,v2:0}];
const WORDS=b=>b?11:6;
let bookSeq=0,bookRows=[],candCache={},bkOpen=LS.bk!=="0";
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)=>`<a href="https://etherscan.io/${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 logoCache={};
async function loadLogos(add