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