memoscan
← all memos

Memo 0x80212662…7accec on Ethereum

0x68575b07…67c7·#25,913,251·view on Etherscan
ken:"0x"+h.slice((row+2*32+12)*2,(row+3*32)*2), quote:"0x"+h.slice((row+3*32+12)*2,(row+4*32)*2), isNFT:f(4)!==0n, anyId:f(5)!==0n, remaining:f(7), initial:f(8), price:f(9), proceeds:f(10), expiry:f(12), tDec:dec(13),qDec:dec(14), tSym:txt(15),qSym:txt(16), }); }catch{continue} } return out; }catch{return out} } async function collectionFloor(collection,quote=ZERO){ if(FLOORVIEW===ZERO||FLOOR===ZERO)return[]; const data="0x"+SEL_COLL_BIDS+encAddr(FLOOR)+encAddr(collection)+encAddr(quote) +encUint(0)+encUint(16)+encUint(256); const raw=await rpc(C,[{to:FLOORVIEW,data},L]).catch(()=>null); if(!raw)return[]; const rows=decBidRows(raw); return rows.filter(r=>r.anyId&&r.remaining>0n) .sort((a,b)=>{const l=b.proceeds*a.remaining,r=a.proceeds*b.remaining;return l>r?1:l<r?-1:0}); } async function floorCandidates(tokenIn,tokenOut,block){ const bin=tokenIn===ZERO?WETH:tokenIn,bout=tokenOut===ZERO?WETH:tokenOut; const data="0x"+SEL_FLOOR_CANDS+encAddr(FLOOR)+encAddr(bin)+encAddr(bout) +encUint(0)+encUint(64)+encUint(512); const raw=await rpc(C,[{to:FLOORVIEW,data},block]).catch(()=>null); if(!raw)return[]; const out=[]; for(const b of decBidRows(raw)){ if(b.isNFT||b.remaining<=0n||b.initial<=0n||b.price<=0n||b.proceeds<=0n)continue; if(b.token.toLowerCase()!==bin.toLowerCase())continue; if(b.quote.toLowerCase()!==bout.toLowerCase())continue; out.push({ id:b.id,board:FLOOR,floor:1,v2:0,dutch:0, maker:b.bidder,pf:1,exp:b.expiry,nA:0,nB:0,cp:ZERO, tA:b.quote,aA:b.proceeds,sA:b.qSym,dA:b.qDec, tB:b.token,aB:b.remaining,sB:b.tSym,dB:b.tDec, price:b.price,initial:b.initial, }); } return out; } function floorGet(o,pay){return o.price*pay/o.initial} function floorPay(o,want){ if(want<=0n)return 0n; const g=(want*o.initial+o.price-1n)/o.price; return g>o.aB?o.aB:g; } function rowGet(o,pay){return o.floor?floorGet(o,pay):o.aA*pay/o.aB} function decViewPage(hex){ const h=strip0x(hex),bytes=h.length/2; const word=b=>{if(b<0||b+32>bytes)throw Error("bad lens page");return BigInt("0x"+h.slice(b*2,(b+32)*2))}; const addr=(b)=>"0x"+h.slice((b+12)*2,(b+32)*2); const arr=Number(word(0)),next=word(32); if(!Number.isSafeInteger(arr)||arr<64||arr+32>bytes)throw Error("bad lens offset"); const n=Number(word(arr)),head=arr+32; if(!Number.isSafeInteger(n)||n>256||head+n*32>bytes)throw Error("bad lens length"); const text=(base,k)=>{const off=Number(word(base+k*32)),p=base+off,len=Number(word(p)); if(!Number.isSafeInteger(off)||!Number.isSafeInteger(len)||p+32+len>bytes)throw Error("bad lens string"); const cap=Math.min(len,1024),a=new Uint8Array(cap); for(let i=0;i<cap;i++)a[i]=parseInt(h.slice((p+32+i)*2,(p+33+i)*2),16); return new TextDecoder().decode(a)}; const rows=[]; const dec=v=>{const d=Number(v);return Number.isInteger(d)&&d>=0&&d<=36?d:null}; for(let i=0;i<n;i++){ try{ const off=Number(word(head+i*32)),b=head+off; if(!Number.isSafeInteger(off)||b+16*32>bytes)throw Error("bad lens row"); rows.push({id:word(b),maker:addr(b+32),pf:word(b+64)===1n,exp:word(b+96), nA:word(b+128)===1n,nB:word(b+160)===1n,cp:addr(b+192), tA:addr(b+224),aA:word(b+256),sA:safeSym(text(b,9)),dA:dec(word(b+320)), tB:addr(b+352),aB:word(b+384),sB:safeSym(text(b,13)),dB:dec(word(b+448)),board:addr(b+480)}); }catch{continue} } return{rows,next}; } const betterRate=(a,b)=>a.aA*b.aB>b.aA*a.aB; const bookShare=(h,amm)=>{ const rest=h.ammIn!==undefined?h.ammIn:h.ammOut; if(!rest)return"Orderbook"; const mine=h.ammIn!==undefined?h.bookIn:h.bookOut; const pct=Number(mine*100n/(mine+rest)); return`Orderbook ${pct<1?"<1":pct}% + ${amm}`; }; const MAX_BOOK_LEGS=32,AON_PAIR_SEEDS=24; function planBookExactIn(rows,amountIn,ammOut,ammIn){ if(!amountIn||!ammIn)return null; let c=rows.filter(o=>!o.nA&&!o.nB&&o.aA&&(o.aB||o.dutch)&&(o.pf||o.aB<=amountIn) &&(!o.floor||(o.price>0n&&o.initial>0n)) &&!(account&&o.maker&&o.maker.toLowerCase()===account.toLowerCase())); const candidateCapped=c.length>256; const aonCount=c.reduce((n,o)=>n+!o.pf,0); const aonBounded=aonCount>=3; c.sort((a,b)=>betterRate(a,b)?-1:betterRate(b,a)?1:0); if(c.length>256)c.length=256; const build=(seed=-1,seed2=-1)=>{ let rem=amountIn,bin=0n,bout=0n,fills=[],legCapped=false; const take=o=>{ if(o.aA*ammIn<=ammOut*o.aB)return; if(!o.pf&&o.aB>rem)return; if(fills.length>=MAX_BOOK_LEGS){legCapped=true;return} if(o.aB===0n){ fills.push({id:o.id,board:o.board,pay:0n,get:o.aA,part:false}); bout+=o.aA;return; } const pay=o.aB<rem?o.aB:rem; if(!pay)return; const get=o.pf?(pay===o.aB?o.aA:rowGet(o,pay)):o.aA; if(!get)return; fills.push({id:o.id,board:o.board,pay,get,part:o.pf&&pay<o.aB}); rem-=pay;bin+=pay;bout+=get; }; if(seed>=0)take(c[seed]); if(seed2>=0)take(c[seed2]); for(let i=0;i<c.length;i++)if(i!==seed&&i!==seed2)take(c[i]); return{fills,bookIn:bin,bookOut:bout,ammIn:rem,score:bout+rem*ammOut/ammIn, bounded:candidateCapped||aonBounded||legCapped}; }; let best=build(-1); for(let i=0;i<c.length;i++)if(!c[i].pf){ const p=build(i);if(p.score>best.score)best=p; } const aon=[];for(let i=0;i<c.length&&aon.length<AON_PAIR_SEEDS;i++)if(!c[i].pf)aon.push(i); for(let i=0;i<aon.length;i++)for(let j=i+1;j<aon.length;j++){ const p=build(aon[i],aon[j]);if(p.score>best.score)best=p; } return best.fills.length?best:null; } const cheaperRate=(a,b)=>a.aB*b.aA<b.aB*a.aA; function planBookExactOut(rows,amountOut,ammIn,ammOut){ if(!amountOut||!ammOut)return null; let c=rows.filter(o=>!o.nA&&!o.nB&&o.aA&&(o.aB||o.dutch)&&(o.pf||o.aA<=amountOut) &&(!o.floor||(o.price>0n&&o.initial>0n)) &&!(account&&o.maker&&o.maker.toLowerCase()===account.toLowerCase())); const candidateCapped=c.length>256; const aonCount=c.reduce((n,o)=>n+!o.pf,0); const aonBounded=aonCount>=3; c.sort((a,b)=>cheaperRate(a,b)?-1:cheaperRate(b,a)?1:0); if(c.length>256)c.length=256; const build=(seed=-1,seed2=-1)=>{ let left=amountOut,bin=0n,bout=0n,fills=[],legCapped=false; const take=o=>{ if(o.aB*ammOut>=ammIn*o.aA)return; if(!o.pf&&o.aA>left)return; if(fills.length>=MAX_BOOK_LEGS){legCapped=true;return} const want=o.aA<left?o.aA:left; let pay,get; if(o.aB===0n){pay=0n;get=want} else if(want===o.aA){pay=o.aB;get=o.aA} else if(o.floor){ pay=floorPay(o,want); get=floorGet(o,pay); } else{ pay=(want*o.aB+o.aA-1n)/o.aA; if(pay>o.aB)pay=o.aB; get=pay*o.aA/o.aB; } if(!get)return; fills.push({id:o.id,board:o.board,pay,get,part:want!==o.aA}); bin+=pay;bout+=get;left-=get<left?get:left; }; if(seed>=0)take(c[seed]); if(seed2>=0)take(c[seed2]); for(let i=0;i<c.length&&left;i++)if(i!==seed&&i!==seed2)take(c[i]); return{fills,bookIn:bin,bookOut:bout,ammOut:left,score:bin+left*ammIn/ammOut, bounded:candidateCapped||aonBounded||legCapped}; }; let best=build(-1); for(let i=0;i<c.length;i++)if(!c[i].pf){ const p=build(i);if(p.score<best.score)best=p; } const aon=[];for(let i=0;i<c.length&&aon.length<AON_PAIR_SEEDS;i++)if(!c[i].pf)aon.push(i); for(let i=0;i<aon.length;i++)for(let j=i+1;j<aon.length;j++){ const p=build(aon[i],aon[j]);if(p.score<best.score)best=p; } return best.fills.length?best:null; } function encFillPlan(fills,tokenIn,tokenOut,recipient,refundTo,deadline){ let tail=encUint(fills.length); for(const f of fills)tail+=encUint(f.id)+encAddr(f.board)+encUint(f.pay)+encUint(f.get)+encBool(f.part); return"0x"+SEL_FILLPLAN+encAddr(tokenIn)+encAddr(tokenOut)+encAddr(recipient)+encAddr(refundTo) +encUint(deadline)+encUint(192)+tail; } function encFillPlanAndSwap(fills,tokenIn,tokenOut,recipient,refundTo,deadline,ammIn,ammData){ let ft=encUint(fills.length); for(const f of fills)ft+=encUint(f.id)+encAddr(f.board)+encUint(f.pay)+encUint(f.get)+encBool(f.part); const d=strip0x(ammData||"0x"),dt=encUint(d.length/2)+d.padEnd(Math.ceil(d.length/64)*64,"0"); return"0x"+SEL_FILLPLAN_SWAP+encAddr(tokenIn)+encAddr(tokenOut)+encAddr(recipient)+encAddr(refundTo) +encUint(deadline)+encUint(256)+encUint(ammIn)+encUint(256+ft.length/2)+ft+dt; } function encSnwap(tokenIn,amountIn,recipient,tokenOut,minOut,executor,data){ const d=strip0x(data),body=encUint(d.length/2)+d.padEnd(Math.ceil(d.length/64)*64,"0"); return"0x"+SEL_SNWAP+encAddr(tokenIn)+encUint(amountIn)+encAddr(recipient)+encAddr(tokenOut) +encUint(minOut)+encAddr(executor)+encUint(224)+body; } const encCheckpoint=(executor,token,recipient)=>encSnwap( ZERO,0n,recipient,ZERO,0n,executor,"0x"+SEL_CHECKPOINT+encAddr(token) ); const SEL_SWEEP3="dc2c256f"; const encSweep=(token,amount,to)=>chainCfg().sweep4 ?"0x"+SEL_SWEEP+encAddr(token)+encUint(0)+encUint(amount)+encAddr(to) :"0x"+SEL_SWEEP3+encAddr(token)+encUint(amount)+encAddr(to); function encPermit2Hybrid(ammData,fills,tokenIn,tokenOut,recipient,refundTo,deadline,bookIn,bookOut){ const bookData=encFillPlan(fills,tokenIn,tokenOut,recipient,refundTo,deadline); const steps=[encCheckpoint(SWAPBOL,tokenIn,refundTo)]; if(bookIn)steps.push(encSweep(tokenIn,bookIn,SWAPBOL)); steps.push(encSnwap(ZERO,0n,recipient,tokenOut,bookOut,SWAPBOL,bookData)); if(ammData&&ammData!=="0x")steps.push(ammData); steps.push(encSweep(tokenIn,0n,refundTo)); return encCalls(steps); } async function swapCandidates(tokenIn,tokenOut,block){ if(tokenOut===ZERO&&tokenIn.toLowerCase()===WETH.toLowerCase())return[]; const key=tokenIn.toLowerCase()+":"+tokenOut.toLowerCase(),old=candCache[key]; if(old&&Date.now()-old.at<15000&&old.block===block){ old.rows.incomplete=old.incomplete;old.rows.unavailable=old.unavailable; return old.rows; } const snapshotAt=Date.now(); const boardIn=tokenIn===ZERO?WETH:tokenIn; const boardOut=tokenOut===ZERO?WETH:tokenOut; const [lc,le,ld]=await Promise.all([ hasCode(SBVIEW),hasCode(SWAPBOL), DUTCH!==ZERO?hasCode(DUTCH):false ]); if(!lc||!le){ const rows=[];rows.unavailable=true;return rows; } const specs=[...BOARDS()]; if(ld){ specs.push({a:DUTCH,dutch:1,ti:tokenIn}); if(tokenIn===ZERO)specs.push({a:DUTCH,dutch:1,ti:WETH}); } const groups=await Promise.all(specs.map(async b=>{ let cursor=0n,out=[],turns=0,incomplete=false,failed=false; try{do{ const ti=b.dutch?b.ti:boardIn,to=boardOut; const data="0x"+(b.dutch?SEL_DUTCH_CANDS:SEL_CANDS)+encAddr(b.a) +(b.dutch?"":encBool(b.v2))+encAddr(ti)+encAddr(to)+encAddr(SWAPBOL) +encUint(cursor)+encUint(512); const p=decViewPage(await rpc(C,[{to:SBVIEW,data},block])); out.push(...p.rows.map(r=>({...r,v2:b.v2||0,dutch:b.dutch||0}))); cursor=p.next; ++turns; }while(cursor&&turns<64)}catch{failed=true;incomplete=true} incomplete=incomplete||!!cursor; return{out,incomplete,failed}; })); let rows=groups.flatMap(g=>g.out); if(tokenIn===ZERO&&tokenOut.toLowerCase()===WETH.toLowerCase()){ rows=rows.filter(r=>r.dutch&&r.tA.toLowerCase()===WETH.toLowerCase()&&r.tB===ZERO); } else if(await floorRoutable(block)){ rows=rows.concat(await floorCandidates(tokenIn,tokenOut,block).catch(()=>[])); } const incomplete=groups.some(g=>g.incomplete); const unavailable=groups.every(g=>g.failed&&!g.out.length); rows.incomplete=incomplete; rows.unavailable=unavailable; const current=candCache[key],bn=BigInt(block),cn=current?BigInt(current.block):-1n; if(!current||bn>cn||(bn===cn&&snapshotAt>=current.at)){ candCache[key]={at:snapshotAt,block,rows,incomplete,unavailable}; } for(const k in candCache)if(k!==key&&snapshotAt-candCache[k].at>60000)delete candCache[k]; return rows; } async function lensBookRows(my){ const [sv,dv]=await Promise.all([hasCode(SBVIEW),DUTCH!==ZERO?hasCode(DUTCH):false]); if(!sv)return null; const specs=[...BOARDS()]; if(dv)specs.push({a:DUTCH,dutch:1}); const pages=await Promise.all(specs.map(async b=>{ let cursor=0n,out=[],turns=0; do{ const data="0x"+(b.dutch?SEL_RECENT_DUTCH:SEL_RECENT)+encAddr(b.a) +(b.dutch?"":encBool(b.v2))+encUint(cursor)+encUint(80)+encUint(256); const p=decViewPage(await rpc(C,[{to:SBVIEW,data},L])); out.push(...p.rows.map(r=>({...r,v2:b.v2||0,dutch:b.dutch||0}))); cursor=p.next; }while(cursor&&out.length<160&&++turns<64&&my===bookSeq); if(cursor)out.capped=true; return out; })); const rows=pages.flat(); let capped=pages.some(p=>p.capped); try{ for(const r of decViewPage(await rpc(C,[{to:SBVIEW, data:"0x"+SEL_EXPIRED+encAddr(SB2)+encUint(0)+encUint(80)+encUint(256)},L])).rows) if(r.maker&&r.maker.toLowerCase()===me())rows.push({...r,v2:1,dutch:0}); }catch{} if(FLOOR!==ZERO&&FLOORVIEW!==ZERO){ await floorRoutable(L); try{ const raw=await rpc(C,[{to:FLOORVIEW, data:"0x"+SEL_RECENT_FLOOR+encAddr(FLOOR)+encUint(0)+encUint(80)+encUint(256)},L]); for(const b of decBidRows(raw)){ if(b.remaining<=0n)continue; rows.push({ id:b.id,board:FLOOR,floor:1,v2:0,dutch:0, maker:b.bidder,pf:b.isNFT?0:1,exp:b.expiry, nA:0,nB:0,cp:ZERO, tA:b.quote,aA:b.proceeds,sA:b.qSym,dA:b.qDec, tB:b.token,aB:b.remaining,sB:b.tSym,dB:b.isNFT?0:b.tDec, price:b.price,initial:b.initial,anyId:b.anyId,isNFT:b.isNFT?1:0, }); } }catch{} } if(dv&&account)try{ const total=Number(BigInt(await rpc(C,[{to:DUTCH,data:"0x"+SEL_NEXTID2},L])||"0x0")); const ids=[];for(let i=total-1;i>=Math.max(0,total-40);--i)ids.push(i); if(ids.length){ const res=await mc3Deep(ids.map(id=>({to:DUTCH,data:"0x"+SEL_DUTCH_LISTING+encUint(id)}))); const now=BigInt(Math.floor(Date.now()/1e3)),me2=me(); for(let i=0;i<ids.length;i++){ const s=strip0x(res[i]||"0x");if(s.length<704)continue; const w=k=>BigInt("0x"+s.slice(k*64,(k+1)*64)),ad=k=>"0x"+s.slice(k*64+24,(k+1)*64); if(ad(0).toLowerCase()!==me2)continue; const exp=w(10);if(!exp||now<=exp)continue; const rem=w(9);if(!rem)continue; rows.push({id:ids[i],board:DUTCH,dutch:1,v2:0,maker:ad(0),pf:1,exp, nA:0,nB:0,cp:ZERO,tA:ad(4),aA:rem,sA:null,dA:null,tB:ad(6),aB:0n,sB:null,dB:null}); } }}catch{} if(FLOOR!==ZERO&&account)try{ const total=Number(BigInt(await rpc(C,[{to:FLOOR,data:"0x"+SEL_NEXTID2},L])||"0x0")); const ids=[];for(let i=total-1;i>=Math.max(0,total-40);--i)ids.push(i); if(ids.length){ const res=await mc3Deep(ids.map(id=>({to:FLOOR,data:"0x"+SEL_BIDS+encUint(id)}))); const now=BigInt(Math.floor(Date.now()/1e3)),me2=me(); for(let i=0;i<ids.length;i++){ const s=strip0x(res[i]||"0x");if(s.length<704)continue; const w=k=>BigInt("0x"+s.slice(k*64,(k+1)*64)),ad=k=>"0x"+s.slice(k*64+24,(k+1)*64); if(ad(0).toLowerCase()!==me2)continue; const exp=w(2)+w(3);if(!exp||now<=exp)continue; const rem=w(10);if(!rem)continue; rows.push({id:ids[i],board:FLOOR,floor:1,v2:0,dutch:0,maker:ad(0),pf:1,exp, nA:0,nB:0,cp:ZERO,tA:ad(6),aA:w(8),sA:null,dA:null,tB:ad(4),aB:rem,sB:null,dB:null}); } }}catch{} const uniq=new Set(),deduped=[]; deduped.capped=capped; for(const r of rows){ const k=`${(r.board||"").toLowerCase()}|${r.dutch?"d":r.floor?"f":"o"}|${r.id}`; if(uniq.has(k))continue; uniq.add(k);deduped.push(r); } return deduped; } const loadBook=()=>loadBookRun().catch(()=>{}); async function loadBookRun(){ const my=++bookSeq; if(tab!=="book"){book.innerHTML="";bookRows=[];syncBook(0);return} const live=(await Promise.all(BOARDS().map(b=> rpc("eth_getCode",[b.a,L]).then(c=>(c||"").length>4?b:null,()=>null)))).filter(Boolean); if(my!==bookSeq)return; if(!live.length){ bookRows=[];bookLegs=[]; book.innerHTML='<h6><span>Orderbook</span></h6><div class="o"><span class="l"><i>No Swapboard deployed on this network yet.</i></span></div>'; syncBook(0);book.classList.remove("hide");return} let rows=null; try{rows=await lensBookRows(my)}catch{rows=null} if(my!==bookSeq)return; bookTrunc=!!(rows&&rows.capped); if(!rows)rows=[]; for(const b of rows.length?[]:live){ let total=0; try{total=Number(BigInt(await rpc(C,[{to:b.a,data:"0x"+SEL_NEXTID},L])))}catch{continue} if(!total)continue; const hi=total,lo=Math.max(0,hi-40); if(lo>0)bookTrunc=true; const ids=[];for(let i=hi-1;i>=lo;--i)ids.push(i); let raw=""; try{raw=await rpc(C,[{to:b.a,data:"0x"+SEL_GETORDERS+encUint(32)+encUint(ids.length)+ids.map(i=>encUint(i)).join("")},L])}catch{continue} if(my!==bookSeq)return; const h=strip0x(raw);if(h.length<128)continue; const w=WORDS(b.v2),words=(h.length-128)/64; let n=Number(BigInt("0x"+h.slice(64,128))); if(!Number.isSafeInteger(n)||n*w>words)n=Math.floor(words/w); const at=(r,k)=>BigInt("0x"+h.slice(128+(r*w+k)*64,128+(r*w+k+1)*64)); const ad=(r,k)=>"0x"+h.slice(128+(r*w+k)*64+24,128+(r*w+k+1)*64); for(let r=0;r<n&&r<ids.length;++r){ if(at(r,1)!==1n)continue; const o=b.v2 ?{id:ids[r],board:b.a,v2:1,maker:ad(r,0),pf:at(r,2)===1n,exp:at(r,3), nA:at(r,4)===1n,nB:at(r,5)===1n,cp:ad(r,6), tA:ad(r,7),aA:at(r,8),tB:ad(r,9),aB:at(r,10)} :{id:ids[r],board:b.a,v2:0,maker:ad(r,0),pf:0,exp:0n,nA:0,nB:0, cp:ZERO,tA:ad(r,2),aA:at(r,3),tB:ad(r,4),aB:at(r,5)}; if(o.exp&&BigInt(Math.floor(Date.now()/1e3))>o.exp &&o.maker.toLowerCase()!==me())continue; if(o.cp!==ZERO &&o.cp.toLowerCase()!==me() &&o.maker.toLowerCase()!==me())continue; rows.push(o); } } if(my!==bookSeq)return; const cutoff=BigInt(Math.floor(Date.now()/1e3)); rows=rows.filter(o=>{ const mine=o.maker.toLowerCase()===me(); if(o.exp&&cutoff>o.exp&&!mine)return false; return o.cp===ZERO ||o.cp.toLowerCase()===me() ||o.maker.toLowerCase()===me(); }); const dutchRows=rows.filter(r=>r.dutch); const bidRows=rows.filter(r=>r.floor); const fillRows=rows.filter(r=>!r.dutch&&!r.floor&&r.v2&&r.pf&&!r.nA&&!r.nB); if(dutchRows.length||bidRows.length||fillRows.length){ const reqs=[ ...dutchRows.map(r=>({to:DUTCH,data:"0x"+SEL_DUTCH_LISTING+encUint(r.id)})), ...bidRows.map(r=>({to:FLOOR,data:"0x"+SEL_BIDS+encUint(r.id)})), ...fillRows.map(r=>({to:r.board,data:"0x"+SEL_INITIAL_B+encUint(r.id)})), ]; const raw=await mc3Deep(reqs); if(my!==bookSeq)return; let k=0; for(const r of dutchRows){const x=raw[k++];if(!x)continue; const h=strip0x(x),w=n=>BigInt("0x"+h.slice(n*64,(n+1)*64)); if(h.length>=640)Object.assign(r,{ start:w(2),duration:w(3),startPrice:w(5),endPrice:w(7),initial:w(8),remaining:w(9)}); } for(const r of bidRows){const x=raw[k++];if(!x)continue; const h=strip0x(x),w=n=>BigInt("0x"+h.slice(n*64,(n+1)*64)); if(h.length>=11*64)Object.assign(r,{start:w(2),duration:w(3),startPrice:w(5),endPrice:w(7)}); } for(const r of fillRows){const x=raw[k++];if(!x)continue; const h=strip0x(x);if(h.length<64)continue; const init=BigInt("0x"+h.slice(0,64)); if(init>r.aB)r.filled=(init-r.aB)*10000n/init; } } if(!rows.length){bookRows=[];bookLegs=[];book.innerHTML="";syncBook(0);return} const needMeta=[]; for(const r of rows){ if(!r.nA&&(r.sA==null||r.dA==null))needMeta.push(r.tA); if(!r.nB&&(r.sB==null||r.dB==null))needMeta.push(r.tB); } await Promise.all([loadLogos(rows.flatMap(r=>[r.tA,r.tB])),warmMeta(needMeta)]); if(my!==bookSeq)return; const legs=await Promise.all(rows.map(r=>Promise.all([ leg(r.tA,r.aA,r.nA,r.sA!=null&&r.dA!=null?{sym:r.sA,dec:r.dA}:null), leg(r.tB,r.aB,r.nB,r.sB!=null&&r.dB!=null?{sym:r.sB,dec:r.dB}:null) ]))); if(my!==bookSeq)return; rows.forEach((r,i)=>{const[a,b]=legs[i]; if(!r.nA&&r.dA==null&&a.dec!=null)r.dA=a.dec; if(!r.nB&&r.dB==null&&b.dec!=null)r.dB=b.dec}); bookRows=rows;bookLegs=legs; paintBook(); } let bookPairOnly=LS.bkp!=="0",bookLegs=[]; let bookType=/^(all|limit|dutch|bid|nft)$/.test(LS.bkt||"")?LS.bkt:"all"; const unitPx=r=>r.aA&&r.aB&&!r.nA&&!r.nB&&!r.isNFT ?r.aB*10n**BigInt(18+(r.dA??18))/(r.aA*10n**BigInt(r.dB??18)):null; const sameAsset=(a,b)=>{ const w=x=>{x=(x||"").toLowerCase();return x===ZERO.toLowerCase()?WETH.toLowerCase():x}; return w(a)===w(b); }; const rowInPair=r=>{ const f=TOKENS[fromSel.value]?.addr,t=TOKENS[toSel.value]?.addr; if(!f||!t)return true; return (sameAsset(r.tA,f)&&sameAsset(r.tB,t))||(sameAsset(r.tA,t)&&sameAsset(r.tB,f)); }; const rowIsType=r=>bookType==="all" ||(bookType==="dutch"&&r.dutch) ||(bookType==="bid"&&r.floor) ||(bookType==="nft"&&(r.nA||r.nB||r.isNFT)) ||(bookType==="limit"&&!r.dutch&&!r.floor); const bookOpenCards=()=>{const s=[]; for(const el of book.querySelectorAll('.o.ins[aria-expanded="true"]')) if(el.dataset.k)s.push(el.dataset.k); return s}; const bookReopen=ks=>{ for(const k of ks){ const el=book.querySelector('.o.ins[data-k="'+k+'"]'); if(!el)continue; const at=k.lastIndexOf(":"); const c=cardCache[k.slice(0,at).toLowerCase()+":"+k.slice(at+1)]; const box=el.parentNode.querySelector(".ic"); if(!c||!box)continue; el.setAttribute("aria-expanded","true"); box.innerHTML=cardHtml(c)||"No card for this order."; }}; const bookOrder=rows=>{ const idx=rows.map((r,i)=>i); const f=TOKENS[fromSel.value]?.addr,t=TOKENS[toSel.value]?.addr; if(!bookPairOnly||!f||!t)return idx; const k=rows.map(r=>{const d=sameAsset(r.tA,t)&&sameAsset(r.tB,f)?0:1; return[d,d?null:unitPx(r)]}); const px=(a,b)=>a===null||b===null?(a===b?0:a===null?1:-1):a<b?-1:a>b?1:0; return idx.sort((a,b)=>k[a][0]-k[b][0]||px(k[a][1],k[b][1])); }; function paintBook(){ const rows=bookRows,legs=bookLegs; if(!rows.length){book.innerHTML="";syncBook(0);return} const wasOpen=bookOpenCards(),wasAt=book.scrollTop; let own="",rest="",nOwn=0,nRest=0,hidden=0; const now=BigInt(Math.floor(Date.now()/1e3)); bookOrder(rows).forEach(i=>{ const r=rows[i],[a,bg]=legs[i]||[]; if(!a||!bg)return; const mine=r.maker.toLowerCase()===me(); const expired=!!(r.exp&&now>r.exp); const tags=(expired?'<span class="tg w" title="This order has lapsed and can no longer be filled, but its escrow is still held by the board. Reclaim returns it to you.">expired</span>':"") +(r.nA||r.nB||r.isNFT?'<span class="tg">NFT</span>':"") +(r.v2&&!r.pf&&!r.nA&&!r.nB?'<span class="tg">AON</span>':"") +(r.dutch?'<span class="tg">Dutch</span>':"") +(r.floor?'<span class="tg">Bid</span>':"") +(r.floor&&r.isNFT&&r.anyId?'<span class="tg">any id</span>':"") +(r.filled>0n?`<span class="tg">${r.filled<100n?"<1":Math.floor(Number(r.filled)/100)}% filled</span>`:"") +(r.cp!==ZERO?'<span class="tg">private</span>':"") +(!a.ok||!bg.ok?'<a class="tg w" href="'+ZLIST_URL+'" target="_blank" rel="noopener"' +' title="Not on the staked token list. Its symbol and decimals are read straight from the contract' +' and nothing has vouched for it - check the address before filling. Stake on zList to get a real' +' token listed.">unverified</a>':""); let when=expired?" · lapsed "+rel(Number(now-r.exp))+" ago — escrow still held" :r.exp?" · "+rel(Number(r.exp-now))+" left":""; if(!expired&&r.dutch&&r.duration){ const end=r.start+r.duration; const floor=r.initial?(r.endPrice*r.remaining+r.initial-1n)/r.initial:0n; when=now>=end?` · floor reached: ${trimAmt(floor,r.dB)} ${r.sB}` :` · floor ${trimAmt(floor,r.dB)} ${r.sB} in ${rel(Number(end-now))}`; } if(!expired&&r.floor&&r.duration){ const end=r.start+r.duration; when=now>=end?" · window closed" :` · climbs to ${trimAmt(r.initial?r.endPrice*r.aB/r.initial:r.endPrice,r.dA)} ${r.sA} in ${rel(Number(end-now))}`; } const dset=`data-i="${r.id}" data-b="${r.board}" data-v="${r.v2}" data-d="${r.dutch||0}" data-f="${r.floor||0}"`; const takeable=!r.floor||(!r.isNFT&&floorReady); const shrinkable=mine&&!expired&&((r.dutch&&!r.nA&&!r.nB)||r.floor); const editable=mine&&!expired&&r.v2&&!r.dutch&&!r.floor&&!r.nA&&!r.nB; const act=mine?`<button data-x="c" ${dset}>${expired?"Reclaim":"Cancel"}</button>` +(shrinkable?`<button data-x="w" ${dset}>Shrink</button>`:"") +(editable?`<button data-x="r" ${dset}>Edit</button>`:"") :takeable?`<button data-x="f" ${dset}>${r.floor?"Sell into":"Fill"}</button>` :`<button disabled>${r.isNFT?"NFT bid":"Not routable"}</button>`; const card=r.board.toLowerCase()!==SB1.toLowerCase(); const row=`<div class="ow"><div class="o${card?" ins":""}"${card?` data-k="${r.board}:${r.id}" role="button" tabindex="0" aria-expanded="false" title="Show the on-chain receipt"`:""}>` +`<span class="l">${a.ic}${bg.ic}<span><b>${a.txt} → ${bg.txt}</b>` +`<i>${pxOf(r,a,bg)}${a.link}${tags}${when}</i></span></span>${act}</div><div class="ic"></div></div>`; if(mine){own+=row;++nOwn;return} if(!(bookPairOnly?rowInPair(r):true)||!rowIsType(r)){++hidden;return} rest+=row;++nRest; }); let html=""; if(nOwn)html+=`<h6><span>Your orders</span><span>${nOwn}</span></h6>`+own; html+=bookFilters(hidden); if(nRest)html+=rest; else html+=`<div class="lqempty">${emptyBook()}${hidden?` ${hidden} hidden by the filters.`:""}</div>`; book.innerHTML=html; bookReop