memoscan
← all memos

Memo 0xedbc2e66…a09f8d on Ethereum

0xacfba7ce…4082·#25,751,344·view on Etherscan
;i++){ const q=res[i*2],c=res[i*2+1]; if(!q)continue; const h=strip0x(q); if(h.length<128)continue; const p2="0x"+h.slice(24,64),o2=BigInt("0x"+h.slice(64,128)); if(o2<=0n||/^0x0{40}$/i.test(p2))continue; if(o2<=out)continue; pool=p2;out=o2;fA=vars[i].tokenIn;won=vars[i]; try{if(c)n=Number(BigInt(c))}catch{n=0} } if(!pool||!won)return null; const tA=won.c0===fA?won.c1:won.c0; let fee=0n; try{const [fx]=await mc3Deep([{to:PPLENS, data:"0x"+SEL_EFFFEE+encAddr(pool)+encAddr(sender)+encAddr(fA)+encUint(amountIn)}],block); if(fx)fee=BigInt(fx)}catch{} return {pool,out,fee,capped:n>LQ_SCAN,tokenIn:fA,tokenOut:tA, unwrap:fA===ZERO&&f.addr!==ZERO?amountIn:0n, wrap:fA!==ZERO&&f.addr===ZERO?amountIn:0n, outDiffers:(tA===ZERO)!==(t.addr===ZERO)}; } const SEL_BBS="e7798987"; const HUBS=()=>[WETH,...NUMERAIRES,"0x2260fac5e5542a773aa44fbcfedf7c193bc2c599","0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0"]; async function hubRoute(f,t,amt,sb,dl,to,rf,block){ const nf=(f.addr===ZERO?WETH:f.addr).toLowerCase(),nt=(t.addr===ZERO?WETH:t.addr).toLowerCase(); const H=HUBS().filter(h=>h.toLowerCase()!==nf&&h.toLowerCase()!==nt); if(!H.length)return null; const E=(x,i,o,n)=>({to:ZQUOTER,data:"0x"+SEL_BBS+encAddr(x)+encBool(false)+encAddr(i)+encAddr(o)+encUint(n)+encUint(sb)+encUint(dl)}); const D=h=>{try{return decQ(h,sb,false,0,4,0,6)}catch{return null}}; const L=[],r1=await mc3Deep(H.map(h=>E(ZROUTER,f.addr,h,amt)),block); for(let i=0;i<H.length;i++){const a=r1[i]&&D(r1[i]);if(a&&a.amountLimit>0n)L.push({h:H[i],a})} if(!L.length)return null; const r2=await mc3Deep(L.map(x=>E(to,x.h,t.addr,x.a.amountLimit)),block); let B=null; for(let i=0;i<L.length;i++){const b=r2[i]&&D(r2[i]); if(b&&b.best.amountOut>0n&&(!B||b.best.amountOut>B.b.best.amountOut))B={...L[i],b}} if(!B)return null; const c=[B.a.callData,B.b.callData,encSweep(B.h,0n,rf),encSweep(ZERO,0n,rf)]; return{best:{source:B.b.best.source,feeBps:B.b.best.feeBps,amountIn:amt,amountOut:B.b.best.amountOut}, callData:encCalls(c),amountLimit:B.b.amountLimit, msgValue:f.addr===ZERO?amt:0n,spender:f.addr===ZERO?null:ZROUTER}; } async function quoteV4Pools(f,t,amountIn,block){ const pools=v4PoolsFor(f,t); if(!pools.length||amountIn<=0n)return null; const rs=(await mc3Deep(pools.map(p=>({to:V4LENS, data:"0x"+SEL_V4QUOTE+encBool(false)+encAddr(f.addr)+encAddr(t.addr) +encUint(p.fee)+encUint(p.ts)+encAddr(p.hooks)+encUint(amountIn)})),block)) .map(h=>{try{const x=strip0x(h||"");return x.length>=128?BigInt("0x"+x.slice(64,128)):0n}catch{return 0n}}); let best=null; for(let i=0;i<pools.length;i++)if(rs[i]>0n&&(!best||rs[i]>best.amountOut)) best={pool:pools[i],amountIn,amountOut:rs[i]}; return best; } const setListNote=t=>{ const el=document.getElementById("listNote"); if(!el)return; el.textContent=t||""; el.classList.toggle("hide",!t)}; const setListSource=w=>setListNote(w?"Built-in token list — "+w:""); let rankFellBack=false; const rankedIds=async()=>{ rankFellBack=false; try{ const ids=decUintArr(await rpc(C,[{to:ZLISTLENS,data:"0x"+SEL_RANKEDIDS},L])); if(ids.length)return ids; }catch{} try{ const ids=decUintArr(await rpc(C,[{to:TOKENLIST,data:"0x"+SEL_RANKEDIDS},L])); if(ids.length)rankFellBack=true; return ids; }catch{return[]} }; async function loadTokenList(){ if(TOKENLIST===ZERO)return setListSource("registry address not set"); try{ const ids=await rankedIds(); if(!ids.length)return setListSource("registry is empty"); const raw=await mc3Deep(ids.slice(0,64).map(id=>({to:TOKENLIST,data:"0x"+SEL_JSON+encUint(id)}))); const rows=raw.map(h=>{try{return h?decStr(h):null}catch{return null}}); const next=[]; for(const row of rows){ if(!row)continue; let t;try{t=JSON.parse(row)}catch{continue} if(t.k!=="eip155")continue; const std=t.p==="Native"||t.p==="ERC-20"?"ft":t.p==="ERC-721"?"nft":""; if(!std)continue; if(!/^0x[0-9a-f]{40}$/i.test(t.a||""))continue; if(!Number.isInteger(t.d)||t.d<0||t.d>36)continue; if(next.some(x=>x.addr.toLowerCase()===t.a.toLowerCase()))continue; const sym=safeSym(t.s).slice(0,12); const logo=safeUrl(t.l); next.push({sym,addr:t.a.toLowerCase(),dec:t.d,std,icon:logo?`<img src="${logo}" width="20" height="20">`:genIcon(sym), name:String(t.n??"").slice(0,64),desc:String(t.desc??"").slice(0,240), v4:parseV4Pools(t.e,t.a)}); } next.sort((a,b)=>(a.std==="nft")-(b.std==="nft")); if(!next.some(t=>t.std==="ft"))return setListSource("registry returned nothing swappable"); const mine=TOKENS.filter(t=>t.cust&&!next.some(x=>x.addr===t.addr.toLowerCase())); const anchors=BUILTIN.filter(b=>(b.addr===ZERO||b.addr===WETH)&&!next.some(x=>x.addr===b.addr)); TOKENS.splice(0,TOKENS.length,...next,...anchors,...mine); setListNote(rankFellBack?"Ranked by listing order — conviction unavailable":""); }catch(e){setListSource("could not be reached")} } const strip0x=h=>h.startsWith("0x")?h.slice(2):h; const pad32=h=>strip0x(h).padStart(64,"0"); const encUint=v=>pad32(BigInt(v).toString(16)); const encAddr=a=>pad32(a.toLowerCase()); const isAddr=a=>/^0x[0-9a-fA-F]{40}$/.test(a||""); const decStr=hex=>{const h=strip0x(hex);const n=parseInt(h.slice(64,128),16); return new TextDecoder().decode(hexToBytes(h.slice(128,128+n*2)))}; const decUintArr=hex=>{const h=strip0x(hex);const n=parseInt(h.slice(64,128),16);const o=[]; for(let i=0;i<n;i++)o.push(BigInt("0x"+h.slice(128+i*64,192+i*64)));return o}; const encBool=b=>pad32(b?"1":"0"); const toHex=v=>"0x"+BigInt(v).toString(16); const parseUnits=(s,dec)=>{ const t=s.trim().replace(/,/g,""); if(!/^(?:\d+|\d*\.\d+|\d+\.)$/.test(t))throw Error("invalid amount"); const[ip,fp=""]=t.split("."); if(fp.length>dec)throw Error("too many decimals"); return BigInt((ip||"0")+fp.padEnd(dec,"0")); }; const formatUnits=(v,dec)=>{ if(!dec)return BigInt(v).toString(); const s=BigInt(v).toString().padStart(dec+1,"0"); const ip=s.slice(0,-dec); const fp=s.slice(-dec).replace(/0+$/,""); return fp?`${ip}.${fp}`:ip; }; const trimAmt=(v,dec)=>{const s=formatUnits(v,dec),i=s.indexOf(".");if(i<0)return s;const r=s.slice(0,i+7).replace(/\.?0+$/,"");return r==="0"&&v?s:r}; const maxAmt=(v,dec)=>{v=BigInt(v);if(dec<=6)return formatUnits(v,dec); const u=10n**BigInt(dec-6);return formatUnits((v+u-1n)/u,6)}; const fitFont=el=>{ const l=el.value.length,s=l>18?".9em":l>14?"1.1em":l>10?"1.25em":"1.65em"; if(el.style.fontSize!==s)el.style.fontSize=s; }; const decQ=(hex,slip,eo,u,v,S,mv)=>{ const h=strip0x(hex),num=i=>BigInt("0x"+h.slice(i*64,(i+1)*64)),E=()=>{throw Error("bad quote")}; if(h.length<(v+2)*64)E(); const o=Number(num(v))*2;if(o+64>h.length)E(); const n=Number(BigInt("0x"+h.slice(o,o+64)));if(!n||o+64+n*2>h.length)E(); const g=num(u+3),t=g>0n,k=S?(num(3)>0n?0:u):t?u:0,B=10000n,ai=S?num(2)+num(u+2):num(2),ao=S?num(3)+g:t?g:num(3),q=eo?ai:ao; const src=[];for(let w=0;w<=u;w+=4)if(num(w+2)>0n||num(w+3)>0n)src.push(Number(num(w))); return{best:{source:Number(num(k)),feeBps:num(k+1),amountIn:ai,amountOut:ao},sources:src,callData:"0x"+h.slice(o+64,o+64+n*2),amountLimit:eo?(q*(B+slip)+B-1n)/B:q*(B-slip)/B,msgValue:num(mv||v+1),slipBps:slip}; }; const merge=(est,exe)=>{if(!est)return exe;if(!exe)return null;return{...exe,best:est.best}}; const NO_WALLET="No wallet detected."; const rpc=(method,params)=>window.ethereum ?window.ethereum.request({method,params}) :Promise.reject(Object.assign(Error(NO_WALLET),{noWallet:true})); const rpcRead=async(method,params,tries=4)=>{ let last; for(let i=0;i<tries;i++){ try{return await rpc(method,params)} catch(e){last=e;await new Promise(r=>setTimeout(r,300*(i+1)))} } throw last; }; const okRet=h=>{h=strip0x(h);if(h&&BigInt("0x"+h)===0n)throw Error("token returned false")}; const waitTx=async hash=>{ const end=Date.now()+600000; let misses=0,wait=1500; while(Date.now()<end){ try{ const r=await rpc("eth_getTransactionReceipt",[hash]); misses=0; if(r){ if(r.status!=="0x1")throw Error("tx reverted: "+hash); return r; } wait=Math.min(wait+500,5000); }catch(e){ if(/tx reverted/.test(e.message||""))throw e; if(++misses>2)wait=Math.min(wait*2,15000); } await new Promise(res=>setTimeout(res,wait)); } throw Error("tx not mined in time"); }; const checkWallet=async()=>{if(!account)throw Error("Connect a wallet first.");const[c,a]=await Promise.all([rpc(I,[]),rpc("eth_accounts",[])]);if(+c!==1||a[0]?.toLowerCase()!==account.toLowerCase())throw Error("wallet changed")}; const hexToBytes=h=>{const s=strip0x(h);const o=new Uint8Array(s.length/2);for(let i=0;i<o.length;i++)o[i]=parseInt(s.slice(i*2,i*2+2),16);return o}; const KRC=[1n,0x8082n,0x800000000000808an,0x8000000080008000n,0x808bn,0x80000001n,0x8000000080008081n,0x8000000000008009n,0x8an,0x88n,0x80008009n,0x8000000an,0x8000808bn,0x800000000000008bn,0x8000000000008089n,0x8000000000008003n,0x8000000000008002n,0x8000000000000080n,0x800an,0x800000008000000an,0x8000000080008081n,0x8000000000008080n,0x80000001n,0x8000000080008008n]; const KR=[0,1,62,28,27,36,44,6,55,20,3,10,43,25,39,41,45,15,21,8,18,2,61,56,14]; const KM=(1n<<64n)-1n; const krol=(x,n)=>n?((x<<BigInt(n))|(x>>BigInt(64-n)))&KM:x; const KCc=new Array(5),KB=new Array(25); const keccak=b=>{const A=new Array(25).fill(0n),rate=136; const pad=rate-(b.length%rate),buf=new Uint8Array(b.length+pad);buf.set(b); buf[b.length]|=1;buf[buf.length-1]|=0x80; for(let o=0;o<buf.length;o+=rate){ for(let i=0;i<17;i++){let v=0n;for(let j=7;j>=0;j--)v=(v<<8n)|BigInt(buf[o+i*8+j]);A[i]^=v} for(let r=0;r<24;r++){ for(let x=0;x<5;x++)KCc[x]=A[x]^A[x+5]^A[x+10]^A[x+15]^A[x+20]; for(let x=0;x<5;x++){const D=KCc[(x+4)%5]^krol(KCc[(x+1)%5],1);for(let y=0;y<25;y+=5)A[x+y]^=D} for(let x=0;x<5;x++)for(let y=0;y<5;y++)KB[y+5*((2*x+3*y)%5)]=krol(A[x+5*y],KR[x+5*y]); for(let x=0;x<5;x++)for(let y=0;y<5;y++)A[x+5*y]=KB[x+5*y]^((~KB[(x+1)%5+5*y]&KM)&KB[(x+2)%5+5*y]); A[0]^=KRC[r]}} let out="";for(let i=0;i<4;i++){let v=A[i];for(let j=0;j<8;j++){out+=Number(v&0xffn).toString(16).padStart(2,"0");v>>=8n}} return "0x"+out}; const namehash=n=>{let nd="0x"+"0".repeat(64); if(n)for(const l of n.split(".").reverse())nd=keccak(new Uint8Array([...hexToBytes(nd),...hexToBytes(keccak(new TextEncoder().encode(l)))])); return nd}; const encStr=v=>{const b=new TextEncoder().encode(v);let h=""; for(const x of b)h+=x.toString(16).padStart(2,"0"); return pad32("20")+encUint(b.length)+h.padEnd(Math.ceil(h.length/64)*64,"0")}; const retAddr=h=>{const s=strip0x(h||""); if(s.length!==64||/[^0-9a-fA-F]/.test(s)||/[^0]/.test(s.slice(0,24)))return""; const a="0x"+s.slice(24).toLowerCase(); return /^0x0{40}$/.test(a)?"":a}; const nameOk=n=>/^[\x21-\x7e]+$/.test(n)&&n.split(".").every(l=>l.length>0); const nsFwd=async(reg,n)=>{const id=await rpc(C,[{to:reg,data:"0x"+SEL_CID+encStr(n)},L]); if(strip0x(id||"").length!==64)return""; return retAddr(await rpc(C,[{to:reg,data:"0x"+SEL_RES+strip0x(id)},L]))}; const dnsEnc=n=>{let h=""; for(const l of n.split(".")){const b=new TextEncoder().encode(l); h+=b.length.toString(16).padStart(2,"0"); for(const x of b)h+=x.toString(16).padStart(2,"0")} return h+"00"}; const encBytes=h=>{h=strip0x(h);return encUint(h.length/2)+h.padEnd(Math.ceil(h.length/64)*64,"0")}; const encResolve=(dns,inner)=>{const a=encBytes(dns); return pad32("40")+encUint(64+a.length/2)+a+encBytes(inner)}; const decBytes=hex=>{const h=strip0x(hex||""); if(h.length<128)return""; const off=Number(BigInt("0x"+h.slice(0,64)))*2; if(!Number.isSafeInteger(off)||off+64>h.length)return""; const len=Number(BigInt("0x"+h.slice(off,off+64)))*2; if(!Number.isSafeInteger(len)||off+64+len>h.length)return""; return "0x"+h.slice(off+64,off+64+len)}; const ensFind=async n=>{const parts=n.split("."), nodes=parts.map((_,i)=>namehash(parts.slice(i).join("."))); const rs=await Promise.all(nodes.map(nd=> rpc(C,[{to:ENSREG,data:"0x"+SEL_RSLV+pad32(nd)},L]).catch(()=>null))); for(let i=0;i<nodes.length;i++){const r=retAddr(rs[i]); if(r)return{r,nd:nodes[i],exact:i===0}} return null}; const ensWild=async r=>{ try{const s=await rpc(C,[{to:r,data:supports(SEL_ERESOLVE)},L]); return strip0x(s||"").length===64&&BigInt(s)===1n}catch{return false}}; const ensFwd=async n=>{const f=await ensFind(n); if(!f)return""; const inner="0x"+SEL_EADDR+pad32(f.nd); if(f.exact)return retAddr(await rpc(C,[{to:f.r,data:inner},L])); if(!await ensWild(f.r))return""; try{return retAddr(decBytes(await rpc(C,[{to:f.r, data:"0x"+SEL_ERESOLVE+encResolve(dnsEnc(n),inner)},L])))} catch(e){ if(/556f1830/i.test((e&&e.data?JSON.stringify(e.data):"")+((e&&e.message)||""))) throw Object.assign(Error(NAME_OFFCHAIN),{offchain:true}); return""; }}; const ensRevName=async(nd,r)=>{ if(!r)return""; try{return decodeString(await rpc(C,[{to:r,data:"0x"+SEL_ENAME+pad32(nd)},L])).trim()}catch{return""}}; const nameFwd=n=>/\.gwei$/i.test(n)?nsFwd(GNS,n):/\.wei$/i.test(n)?nsFwd(WNS,n):/\.eth$/i.test(n)?ensFwd(n):Promise.resolve(""); const FWD_TTL=30000; const fwdCache=new Map(); const nameFwdCached=n=>{const hit=fwdCache.get(n); if(hit&&hit.exp>Date.now())return hit.p; const p=nameFwd(n); fwdCache.set(n,{p,exp:Date.now()+FWD_TTL}); p.catch(()=>fwdCache.delete(n)); return p}; const nameRev=async a=>{ const nd=namehash(strip0x(a).toLowerCase()+".addr.reverse"); const [w,g,er]=await Promise.all([ {to:WNS,data:"0x"+SEL_REV+encAddr(a)}, {to:GNS,data:"0x"+SEL_REV+encAddr(a)}, {to:ENSREG,data:"0x"+SEL_RSLV+pad32(nd)}] .map(c=>rpc(C,[c,L]).catch(()=>null))); const str=h=>{try{return h?decodeString(h).trim():""}catch{return""}}; const agrees=async n=>{ if(!n||!nameOk(n.toLowerCase()))return""; let f="";try{f=await nameFwdCached(n.toLowerCase())}catch{} return f&&f.toLowerCase()===a.toLowerCase()?n:""}; return await agrees(str(w))||await agrees(str(g)) ||await agrees(await ensRevName(nd,retAddr(er)))||""}; const decodeString=hex=>{ const h=strip0x(hex); if(h.length<=64)return new TextDecoder().decode(hexToBytes(h)).replace(/\0+$/,""); const off=Number(BigInt("0x"+h.slice(0,64)))*2; const len=Number(BigInt("0x"+h.slice(off,off+64)))*2; return new TextDecoder().decode(hexToBytes(h.slice(off+64,off+64+len))); }; const kecStr=v=>keccak(new TextEncoder().encode(v)); const ckAddr=a=>{const s=strip0x(a); if(s===s.toLowerCase()||s===s.toUpperCase())return true; const h=strip0x(kecStr(s.toLowerCase())); for(let i=0;i<40;i++){const c=s[i]; if(c>="a"&&c<="f"&&parseInt(h[i],16)>=8)return false; if(c>="A"&&c<="F"&&parseInt(h[i],16)<8)return false} return true}; const domainSep=(name,ver,tok)=>keccak(hexToBytes( kecStr(EIP712DOM)+pad32(kecStr(name))+pad32(kecStr(ver))+encUint(CHAIN_ID)+encAddr(tok))); const permitInfo=async(tok,owner)=>{ try{ const[ds,th,nc,nm]=await mc3Deep([ {to:tok,data:"0x"+SEL_DS},{to:tok,data:"0x"+SEL_PERMIT_TH}, {to:tok,data:"0x"+SEL_NONCES+encAddr(owner)},{to:tok,data:"0x"+SEL_NAME}]); if(!ds||ds==="0x")return null; if(th&&strip0x(th).length>=64&&strip0x(th).slice(0,64).toLowerCase()!==TH_2612)return null; if(!nc||!nm)return null; const nonce=BigInt(nc); const name=decodeString(nm); for(const ver of ["1","2"])if(domainSep(name,ver,tok).toLowerCase()===ds.toLowerCase()) return{name,ver,nonce}; }catch{} return null}; const signPermit=async(tok,info,owner,value,deadline)=>{ const td={types:{EIP712Domain:[{name:"name",type:"string"},{name:"version",type:"string"}, {name:"chainId",type:"uint256"},{name:"verifyingContract",type:"address"}], Permit:[{name:"owner",type:"address"},{name:"spender",type:"address"}, {name:"value",type:"uint256"},{name:"nonce",type:"uint256"},{name:"deadline",type:"uint256"}]}, primaryType:"Permit",domain:{name:info.name,version:info.ver,chainId:CHAIN_ID,verifyingContract:tok}, message:{owner,spender:ZROUTER,value:value.toString(),nonce:info.nonce.toString(),deadline:deadline.toString()}}; const sig=strip0x(await rpc("eth_signTypedData_v4",[owner,JSON.stringify(td)])); if(sig.length!==130)throw Error("bad signature"); let v=parseInt(sig.slice(128,130),16);if(v<27)v+=27; return"0x"+SEL_RPERMIT+encAddr(tok)+encUint(value)+encUint(deadline)+encUint(v)+sig.slice(0,64)+sig.slice(64,128)}; const P2DOM={name:"Permit2",chainId:CHAIN_ID,verifyingContract:PERMIT2}; const p2Ready=async(tok,owner)=>{ try{return BigInt(await rpc(C,[{to:tok,data:"0x"+SEL_ALLOWANCE+encAddr(owner)+encAddr(PERMIT2)},L]))}catch{return 0n}}; const signPermit2=async(tok,owner,amount,deadline)=>{ const nonce=BigInt("0x"+[...crypto.getRandomValues(new Uint8Array(31))].map(b=>b.toString(16).padStart(2,"0")).join("")); const td={types:{EIP712Domain:[{name:"name",type:"string"},{name:"chainId",type:"uint256"}, {name:"verifyingContract",type:"address"}], TokenPermissions:[{name:"token",type:"address"},{name:"amount",type:"uint256"}], PermitTransferFrom:[{name:"permitted",type:"TokenPermissions"},{name:"spender",type:"address"}, {name:"nonce",type:"uint256"},{name:"deadline",type:"uint256"}]}, primaryType:"PermitTransferFrom",domain:P2DOM, message:{permitted:{token:tok,amount:amount.toString()},spender:ZROUTER, nonce:nonce.toString(),deadline:deadline.toString()}}; const sig=strip0x(await rpc("eth_signTypedData_v4",[owner,JSON.stringify(td)])); const n=sig.length/2;if(!Number.isInteger(n)||n===0||n>4096)throw Error("bad signature"); const norm=n===65&&parseInt(sig.slice(128,130),16)<27 ?sig.slice(0,128)+(parseInt(sig.slice(128,130),16)+27).toString(16).padStart(2,"0") :sig; const tail=encUint(n)+norm.padEnd(Math.ceil(norm.length/64)*64,"0"); return"0x"+SEL_P2TF+encAddr(tok)+encUint(amount)+encUint(nonce)+encUint(deadline)+pad32("a0")+tail}; const capsFor=(c,id)=>{ for(const key of Object.keys(c)){ try{if(BigInt(key)===id)return c[key]}catch{} } return undefined}; const hasAtomicBatch=c=>{ if(!c||typeof c!=="object")return false; const own=capsFor(c,BigInt(CHAIN_ID)); const k=own!==undefined?own:capsFor(c,0n); return!!(k&&k.atomic&&(k.atomic.status==="supported"||k.atomic.status==="ready"))}; const canBatch=async owner=>{ try{const c=await rpc("wallet_getCapabilities",[owner]); return hasAtomicBatch(c)}catch{return false}}; const sendBatch=async(owner,calls)=>{ const res=await rpc("wallet_sendCalls",[{version:"2.0.0",chainId:toHex(CHAIN_ID),from:owner,atomicRequired:true,calls}]); const id=typeof res==="string"?res:res.id; for(let i=0;i<600;i++){ try{const st=await rpc("wallet_getCallsStatus",[id]); const rs=(st&&st.receipts)||[]; if(rs.some(r=>r&&r.status==="0x0"))throw Error("batch reverted"); const last=rs[rs.length-1],x=last&&(last.transactionHash||last.hash); if(x)return x; const q=String(st&&st.status); if(q==="400"||q==="500"||/fail|revert|reject/i.test(q))throw Error("batch failed"); if(q==="200"||/CONFIRMED|SUCCESS/i.test(q))throw Error("batch missing tx"); }catch(e){if(/^batch /.test(e.message))throw e} await new Promise(r=>setTimeout(r,1000))} throw Error("batch timed out")}; const encCalls=cs=>{const n=cs.length;let head="",tail="";let off=n*32; for(const c of cs){const d=strip0x(c),len=d.length/2; head+=encUint(off);const body=encUint(len)+d.padEnd(Math.ceil(d.length/64)*64,"0"); tail+=body;off+=body.length/2} return"0x"+SEL_MULTICALL+pad32("20")+encUint(n)+head+tail}; const MC3="0xcA11bde05977b3631167028862bE2a173976CA11"; const encAgg3=cs=>{const n=cs.length;let head="",tail="",off=n*32; for(const c of cs){const d=strip0x(c.data),len=d.length/2; const body=encAddr(c.to)+encBool(true)+encUint(96)+encUint(len)+d.padEnd(Math.ceil(d.length/64)*64,"0"); head+=encUint(off);tail+=body;off+=body.length/2} return"0x82ad56cb"+pad32("20")+encUint(n)+head+tail}; let mcOk=0,mcFailed=0; const decAgg3=hex=>{const h=strip0x(hex),at=i=>h.slice(i*64,(i+1)*64); const n=Number(BigInt("0x"+at(1))),out=[]; for(let i=0;i<n;i++){const rel=Number(BigInt("0x"+at(2+i)))/32,w=2+rel; const ok=BigInt("0x"+at(w))===1n,bo=Number(BigInt("0x"+at(w+1)))/32,lw=w+bo; const len=Number(BigInt("0x"+at(lw))); if(ok)mcOk++;else mcFailed++; out.push(ok?"0x"+h.slice((lw+1)*64,(lw+1)*64+len*2):null)} return out}; const mc3=async(cs,tag=L)=>cs.length?decAgg3(await rpc(C,[{to:MC3,data:encAgg3(cs)},tag])):[]; let mc3Cap=4,mc3Won=0; const MC3_MAX=16; const mc3Split=(cs,n)=>{const o=[];for(let i=0;i<cs.length;i+=n)o.push(cs.slice(i,i+n));return o}; const MC3_LANES=6; let mc3Live=0; const mc3Waiting=[]; const mc3Gate=async fn=>{ if(mc3Live>=MC3_LANES)await new Promise(r=>mc3Waiting.push(r)); mc3Live++; try{return await fn()} finally{mc3Live--;const next=mc3Waiting.shift();if(next)next()} }; let mcGaveUp=0; const mc3Deep=async(cs,tag=L,depth=0)=>{ if(!cs.length)return[]; const fan=async n=>(await Promise.all(mc3Split(cs,n).map(c=>mc3Deep(c,tag,depth+1)))).flat(); if(cs.length>mc3Cap&&depth<8)return fan(mc3Cap); try{ const r=await mc3Gate(()=>mc3(cs,tag)); if(cs.length>=mc3Cap&&mc3Cap<MC3_MAX&&r.every(x=>x!=null)&&++mc3Won>=4){mc3Cap++;mc3Won=0} return r; } catch(e){ mc3Won=0; if(cs.length<2||depth>=8){mcGaveUp+=cs.length;return cs.map(()=>null)} const h=cs.length>>1; if(h<mc3Cap)mc3Cap=h; await new Promise(r=>setTimeout(r,150*(depth+1))); return fan(h); } }; const REF_N=100n; const IMPACT_HIDE=50n, IMPACT_WARN=500n, IMPACT_CONFIRM=1500n, IMPACT_TYPED=3000n; const impactBps=(v,ref,eo)=>{if(!v||!ref)return null;const lin=ref*REF_N; if(lin===0n)return 0n; return eo?(v<=lin?0n:(v-lin)*10000n/lin):(v>=lin?0n:(lin-v)*10000n/lin)}; const safeSym=s=>String(s??"").replace(/[<>&"'`]/g,"") .replace(/[\u0000-\u001f\u007f]/g,"").trim().slice(0,16)||"?"; const safeUrl=u=>{u=String(u??"").trim(); return u.length<=32768&&/^(?:https:\/\/|data:image\/[a-z.+-]+;base64,)[^\s<>"'`\\]+$/i.test(u)?u:""}; const genIcon=sym=>`<svg width="20" height="20" viewBox="0 0 32 32"><circle cx="16" cy="16" r="16" fill="#999"/><text x="16" y="21" text-anchor="middle" fill="#fff" font-size="13" font-weight="600" font-family="system-ui">${(safeSym(sym)[0]||"?").toUpperCase()}</text></svg>`; const STORE="zswap:custom"; const SEL_SUPPORTS="01ffc9a7",IFACE_721="80ac58cd",IFACE_1155="d9b67a26"; const supports=id=>"0x"+SEL_SUPPORTS+id.padEnd(64,"0"); async function addCustomToken(a,keep=1){ a=a.toLowerCase(); if(!/^0x[0-9a-f]{40}$/.test(a))throw Error("invalid address"); const i=TOKENS.findIndex(t=>t.addr.toLowerCase()===a); if(i>=0)return i; if((await rpc("eth_getCode",[a,L])).length<5)throw Error("no contract at that address"); const[symRet,decRet,is721,is1155]=await mc3Deep([ {to:a,data:"0x95d89b41"},{to:a,data:"0x313ce567"}, {to:a,data:supports(IFACE_721)},{to:a,data:supports(IFACE_1155)}]); let uriRet=null; try{uriRet=await rpc(C,[{to:a,data:"0x"+SEL_CONTRACTURI},L])}catch{} const yes=h=>{try{return !!h&&BigInt(h)===1n}catch{return false}}; let sym="?"; try{sym=decodeString(symRet||"0x").trim().replace(/[<>&"'`]/g,"").slice(0,16)||"?"}catch{} if(TOKENS.some(t=>t.sym.toLowerCase()===sym.toLowerCase()))sym+=" "+a.slice(0,6)+"\u2026"; if(yes(is1155)&&!yes(is721))throw Error("ERC-1155 is not supported"); const std=yes(is721)?"nft":"ft"; let dec=0; if(std==="ft"){ dec=Number(BigInt(decRet||"0x0")); if(!decRet||!Number.isInteger(dec)||dec<0||dec>36)throw Error("not an ERC-20 or ERC-721"); } let selfArt="";try{selfArt=imgFromContractURI(uriRet?decodeString(uriRet):"")}catch{} if(selfArt)logoCache[a]=selfArt; TOKENS.push({sym,addr:a,dec,std,cust:1, icon:selfArt?`<img width="20" height="20" style="border-radius:50%;object-fit:cover" src="${selfArt}" alt="">`:genIcon(sym)}); if(keep)try{const arr=JSON.parse(localStorage.getItem(STORE)||"[]") .filter(x=>String(x?.addr||"").toLowerCase()!==a); arr.push({sym,addr:a,dec,std});localStorage.setItem(STORE,JSON.stringify(arr))}catch{} return TOKENS.length-1; } try{for(const t of JSON.parse(localStorage.getItem(STORE)||"[]")){ const addr=(t?.addr||"").toLowerCase(); const dec=+t?.dec; if(/^0x[0-9a-f]{40}$/.test(addr)&&Number.isInteger(dec)&&dec>=0&&dec<=36&&!TOKENS.some(x=>x.addr.toLowerCase()===addr)){ const sym=String(t.sym||"?").replace(/[<>&"'`]/g,"").slice(0,16)||"?"; TOKENS.push({sym,addr,dec,std:t.std==="nft"?"nft":"ft",cust:1,icon:genIcon(sym)}); } }}catch{} const QUOTE_TTL=45000; let account=null,connecting=false,last=null,fromBalance=0n,mode="in",bSeq=0,tab="swap",sendReady=null,sSeq=0,sendD="0",bookD="86400"; let quoting=false; let lqMode=false; const readBal=async t=>BigInt(t.addr===ZERO ?await rpc("eth_getBalance",[account,L]) :await rpc(C,[{to:t.addr,data:"0x"+SEL_BALANCEOF+encAddr(account)},L])); const showBal=(el,v,dec)=>{const n=+form