0x68575b07…67c7·#25,913,246·view on Etherscan
>=mc3Cap&&mc3Cap<MC3_MAX&&r.every(x=>x!=null)&&++mc3Won>=2){mc3Cap++;mc3Won=0;mc3Save()}
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;mc3Save()}
await new Promise(r=>setTimeout(r,150*(depth+1)));
return fan(h);
}
};
let bnVal="",bnAt=0,bnJob=null;
const BN_TTL=12000;
const bnFetch=()=>bnJob||(bnJob=rpc("eth_blockNumber",[])
.then(n=>{bnVal=n;bnAt=Date.now();return n})
.finally(()=>{bnJob=null}));
const blockNow=()=>{
const age=Date.now()-bnAt;
if(!bnVal||age>=BN_TTL)return bnFetch();
if(age>BN_TTL/2)bnFetch().catch(()=>{});
return Promise.resolve(bnVal);
};
const codeSeen={};
const CODE_MISS_TTL=60000;
const hasCode=async a=>{
if(codeSeen[a]===true)return true;
const miss=codeSeen[a];
if(miss&&Date.now()-miss<CODE_MISS_TTL)return false;
const h=await rpc("eth_getCode",[a,L]);
const ok=(h||"").length>4;
codeSeen[a]=ok||Date.now();
return ok;
};
const REF_N=100n;
const CH_WICK_CAP=3;
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 safeDataUrl=u=>{u=String(u??"").trim();
return u.length<=32768&&/^data:image\/[a-z.+-]+;base64,[A-Za-z0-9+/=]+$/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=()=>CHAIN_ID===1?"zswap:custom":"zswap:custom:"+CHAIN_ID;
function keepToken(t){
if(!t||!t.addr)return;
const a=t.addr.toLowerCase();
try{const arr=JSON.parse(localStorage.getItem(STORE())||"[]")
.filter(x=>String(x?.addr||"").toLowerCase()!==a);
arr.push({sym:t.sym,addr:a,dec:t.dec,std:t.std,cause:t.cause?1:0});
localStorage.setItem(STORE(),JSON.stringify(arr))}catch{}
}
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 rpcRead("eth_getCode",[a,L],2)).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}};
const dup=TOKENS.findIndex(t=>t.addr.toLowerCase()===a);
if(dup>=0)return dup;
let sym="?";
try{sym=decodeString(symRet||"0x").trim().replace(/[<>&"'`]/g,"").replace(/[\u0000-\u001f\u007f]/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&&decRet!=="0x"?decRet:"0x0"));
if(!decRet||decRet==="0x"||!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)keepToken(TOKENS[TOKENS.length-1]);
return TOKENS.length-1;
}
function restoreCustom(){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,"").replace(/[\u0000-\u001f\u007f]/g,"").slice(0,16)||"?";
TOKENS.push({sym,addr,dec,std:t.std==="nft"?"nft":"ft",cust:1,icon:genIcon(sym),
...(t.cause?{cause:1}:{})});
}
}}catch{}}
restoreCustom();
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=+formatUnits(v,dec);
const pretty=n>=1?n.toLocaleString("en-US",{maximumFractionDigits:4}):sigAmt(v,dec,4);
el.innerHTML=`Balance: ${pretty} <a href="#" title="Use maximum (leaves gas for ETH)">Max</a>`};
async function refreshBalance(){
const f=TOKENS[fromSel.value];
if(!account||!f){bal.textContent="";fromBalance=0n;render();return}
const my=++bSeq;
try{
const v=await readBal(f);
if(my!==bSeq)return;
fromBalance=v;showBal(bal,v,f.dec);
}catch{if(my===bSeq){bal.textContent="";fromBalance=0n}}
refreshBalance1();
render();
}
let toBalance=0n,b1Seq=0;
async function refreshBalance1(){
const t=TOKENS[toSel.value];
bal1.classList.toggle("hide",!lqMode);
if(!lqMode||!account||!t){bal1.textContent="";toBalance=0n;return}
const my=++b1Seq;
try{
const v=await readBal(t);
if(my!==b1Seq)return;
toBalance=v;showBal(bal1,v,t.dec);
}catch{if(my===b1Seq){bal1.textContent="";toBalance=0n}}
}
const optsHtml=()=>{
let ft="",nft="";
let born="";
let cau="";
TOKENS.forEach((t,i)=>{const o=`<option value="${i}">${t.sym}</option>`;
if(t.std==="nft")nft+=o;else if(t.cause)cau+=o;else if(t.launched)born+=o;else ft+=o});
return ft
+(cau?`<optgroup label="Causes — backed and burned, never traded">${cau}</optgroup>`:"")
+(born?`<optgroup label="Launched on zSwap — not curated">${born}</optgroup>`:"")
+(nft?`<optgroup label="NFT collections — auction only">${nft}</optgroup>`:"")
+`<option value="__custom">+ Custom token…</option>`;
};
const rebuild=()=>{for(const s of[fromSel,toSel]){const v=s.value;s.innerHTML=optsHtml();s.value=v}};
const pairAddrs=()=>({fa:TOKENS[fromSel.value]?.addr,ta:TOKENS[toSel.value]?.addr,
fi:String(fromSel.value),ti:String(toSel.value)});
const restorePair=k=>{
if(!k)return;
const at=a=>a?TOKENS.findIndex(t=>t.addr.toLowerCase()===a.toLowerCase()):-1;
const held=(cur,want)=>String(cur)===want||!TOKENS[cur];
if(held(fromSel.value,k.fi)){const i=at(k.fa);if(i>=0)fromSel.value=i}
if(held(toSel.value,k.ti)){const i=at(k.ta);if(i>=0&&String(i)!==String(fromSel.value))toSel.value=i}
for(const s of[fromSel,toSel]){
const want=s.dataset.addr;
if(!want||held(s.value,s===fromSel?k.fi:k.ti))continue;
const i=at(want);
if(i>=0&&(s===fromSel||String(i)!==String(fromSel.value)))s.value=i;
}
};
const isNft=v=>TOKENS[v]?.std==="nft";
let pairChosen=false;
const wrapTwin=(a,b)=>{
const x=(a||"").toLowerCase(),y=(b||"").toLowerCase(),w=WETH.toLowerCase();
return (x===ZERO&&y===w)||(x===w&&y===ZERO);
};
const rankedOutputs=fi=>TOKENS.map((t,i)=>i)
.filter(i=>i!==fi&&!isNft(String(i))&&!wrapTwin(TOKENS[i].addr,TOKENS[fi]?.addr));
const deadOutputs=new Set();
let pairAuto=false;
const rankedDefaults=()=>{
if(TOKENS.length<2)return;
const fi=TOKENS.findIndex((t,i)=>!isNft(String(i)));
if(fi<0)return;
const ti=rankedOutputs(fi).find(i=>!deadOutputs.has(TOKENS[i].addr.toLowerCase()));
if(ti==null)return;
fromSel.value=String(fi);toSel.value=String(ti);
fromSel.dataset.prev=fromSel.value;toSel.dataset.prev=toSel.value;
pairAuto=true;
try{LS["zswap:pair"]=JSON.stringify([TOKENS[fi].addr,TOKENS[ti].addr])}catch{}
};
const me=()=>(account||"").toLowerCase();
const advancePastDeadPair=()=>{
if(!pairAuto||pairChosen)return false;
const cur=TOKENS[toSel.value];
if(!cur)return false;
deadOutputs.add(cur.addr.toLowerCase());
const fi=Number(fromSel.value);
const next=rankedOutputs(fi).find(i=>!deadOutputs.has(TOKENS[i].addr.toLowerCase()));
if(next==null)return false;
toSel.value=String(next);toSel.dataset.prev=toSel.value;
syncDisabled();syncIcons();
return true;
};
const settlePair=keep=>{if(pairChosen)restorePair(keep);else rankedDefaults()};
let listFresh=false;
const adoptList=keep=>{
cbDisarm();
listFresh=true;
rebuild();
settlePair(keep);
if(!TOKENS[fromSel.value])fromSel.value=0;
if(!TOKENS[toSel.value]||toSel.value===fromSel.value)toSel.value=TOKENS[1]?1:0;
syncDisabled();syncIcons();
};
const syncDisabled=keep=>{
const pair=tab!=="send";
const nftOk=tab==="book";
if(pair&&fromSel.value===toSel.value){
const alt=TOKENS.findIndex((t,i)=>String(i)!==fromSel.value&&(nftOk||t.std!=="nft"));
if(alt>=0){toSel.value=String(alt);toSel.dataset.prev=toSel.value}
}
if(pair&&nftOk&&isNft(fromSel.value)&&isNft(toSel.value)){
const give=keep===toSel?fromSel:toSel,stay=give===fromSel?toSel:fromSel;
const alt=TOKENS.findIndex((t,i)=>t.std!=="nft"&&String(i)!==stay.value);
if(alt>=0){give.value=String(alt);give.dataset.prev=give.value}
}
for(const[sel,other]of[[fromSel,toSel],[toSel,fromSel]])
for(const opt of sel.options)
opt.disabled=opt.value!=="__custom"&&((pair&&opt.value===other.value)||(!nftOk&&isNft(opt.value)));
};
fromSel.innerHTML=optsHtml();fromSel.value=0;
toSel.innerHTML=optsHtml();
const LAST_PAIR="zswap:pair";
const seatPair=()=>{
try{
const [a,b]=JSON.parse(LS[LAST_PAIR]||"[]");
const fi=TOKENS.findIndex(t=>t.addr.toLowerCase()===String(a).toLowerCase());
const ti=TOKENS.findIndex(t=>t.addr.toLowerCase()===String(b).toLowerCase());
if(fi>=0&&ti>=0&&fi!==ti){fromSel.value=String(fi);toSel.value=String(ti);return true}
}catch{}
return false;
};
if(!seatPair())toSel.value=String(Math.max(1,TOKENS.findIndex(t=>t.sym==="USDC")));
syncDisabled();
const iconShown=["",""];
const syncIcons=()=>{
[[fromIcon,fromSel],[toIcon,toSel]].forEach(([el,s],i)=>{
const t=TOKENS[s.value],ic=t?.icon||"";
if(iconShown[i]!==ic){iconShown[i]=ic;el.innerHTML=ic}
if(t)s.dataset.addr=t.addr});
syncPickers()};
const tkPanel=document.getElementById("tkPanel"),tkFind=document.getElementById("tkFind"),
tkList=document.getElementById("tkList");
let tkSel=null,tkCur=-1;
const pickBtn=sel=>document.getElementById(sel===fromSel?"fromPick":"toPick");
const syncPickers=()=>{
for(const sel of [fromSel,toSel]){
const b=pickBtn(sel);if(!b)continue;
const t=TOKENS[sel.value];
b.classList.add("on");sel.classList.add("rich");
b.querySelector(".tkbs").textContent=t?t.sym:(sel.selectedOptions[0]?.textContent||"");
b.setAttribute("aria-label",`Token: ${t?t.sym:"none"} - change`);
const pill=b.closest(".pill");
if(pill){
const name=t?.name&&t.name.toLowerCase()!==t.sym.toLowerCase()?t.name:"";
const extra=[name,t?.desc].filter(Boolean);
if(extra.length)pill.title=[`${t.sym}${name?" — "+name:""}`,t.desc].filter(Boolean).join("\n\n");
else pill.removeAttribute("title");
}
}
};
const tkAct=id=>{if(id)tkFind.setAttribute("aria-activedescendant",id);
else tkFind.removeAttribute("aria-activedescendant")};
const tkRows=()=>{
const q=tkFind.value.trim().toLowerCase();
tkList.textContent="";
tkCur=-1;tkAct("");
const frag=document.createDocumentFragment();
let n=0,group="";
for(const opt of tkSel.options){
const t=TOKENS[opt.value];
if(q){
const hay=[opt.textContent,t?.name,t?.addr].filter(Boolean).join(" ").toLowerCase();
if(!hay.includes(q))continue;
}
const g=opt.parentElement?.label||(opt.value==="__custom"?"":
(listLive?(rankFellBack?"zList — listing order":"zList — ranked by conviction"):"Built-in token list"));
if(g&&g!==group){const h=document.createElement("div");h.className="tkg";h.textContent=g;frag.appendChild(h);group=g}
const r=document.createElement("button");
r.type="button";r.className="tkr";r.setAttribute("role","option");
r.id="tkr"+n;
r.dataset.value=opt.value;
r.setAttribute("aria-selected",String(opt.value===tkSel.value));
if(opt.disabled)r.setAttribute("aria-disabled","true");
const ic=document.createElement("span");ic.className="tki";
if(t?.icon)ic.innerHTML=t.icon;
r.appendChild(ic);
const tx=document.createElement("span");tx.className="tkt";
const top=document.createElement("span");top.className="tks";
const b=document.createElement("b");b.textContent=opt.textContent.trim();top.appendChild(b);
if(t?.name&&t.name.toLowerCase()!==b.textContent.toLowerCase()){
const nm=document.createElement("span");nm.className="tkn";nm.textContent=t.name;top.appendChild(nm);
}
tx.appendChild(top);
if(t?.desc){const d=document.createElement("span");d.className="tkd";d.textContent=t.desc;tx.appendChild(d)}
r.appendChild(tx);
if(opt.value===tkSel.value){const m=document.createElement("span");m.className="tkmark";m.textContent="✓";r.appendChild(m)}
frag.appendChild(r);n++;
}
if(!n){
const a=/^0x[0-9a-f]{40}$/.test(q)?q:"";
if(a){
const r=document.createElement("button");
r.type="button";r.className="tkr";
const tx=document.createElement("span");tx.className="tkt";
const top=document.createElement("span");top.className="tks";
const b=document.createElement("b");b.textContent="Use "+short(a);top.appendChild(b);
tx.appendChild(top);
const d=document.createElement("span");d.className="tkd";
d.textContent="Not listed \u2014 read it from chain";
tx.appendChild(d);r.appendChild(tx);
r.onclick=async e=>{
e.stopPropagation();
if(r.dataset.busy)return;
r.dataset.busy="1";b.textContent="Reading\u2026";
try{const i=await addCustomToken(a);rebuild();tkChoose(String(i))}
catch(e){delete r.dataset.busy;b.textContent="Use "+short(a);
d.textContent=(e&&e.message)||"could not read that address"}};
frag.appendChild(r);
}else{const e=document.createElement("div");e.className="tkempty";e.textContent="No token matches that.";frag.appendChild(e)}
}
tkList.classList.toggle("fresh",listFresh);
listFresh=false;
tkList.appendChild(frag);
tkCur=-1;
};
const tkOpen=sel=>{
if(tkSel&&tkSel!==sel)pickBtn(tkSel)?.setAttribute("aria-expanded","false");
tkSel=sel;tkFind.value="";tkRows();
tkPanel.classList.remove("hide");
if(matchMedia("(pointer:coarse)").matches){
tkPanel.style.left=tkPanel.style.top=tkPanel.style.maxHeight=tkPanel.style.width="";
pickBtn(sel).setAttribute("aria-expanded","true");
tkFind.focus({preventScroll:true});
return;
}
const b=pickBtn(sel).getBoundingClientRect();
const card=document.querySelector(".card").getBoundingClientRect();
const w=Math.min(Math.max(card.width,240),innerWidth-16);
tkPanel.style.width=w+"px";
const left=Math.max(8,Math.min(card.left,innerWidth-w-8));
const above=b.top,below=innerHeight-b.bottom;
const flip=below<240&&above>below;
const MENU_MAX=520;
const room=Math.min(MENU_MAX,Math.max(140,(flip?above:below)-14));
tkPanel.style.maxHeight=room+"px";
const h=Math.min(tkPanel.offsetHeight,room);
const top=flip?b.top-h-6:b.bottom+6;
tkPanel.style.left=left+"px";
tkPanel.style.top=Math.max(8,Math.min(top,innerHeight-h-8))+"px";
pickBtn(sel).setAttribute("aria-expanded","true");
tkFind.focus({preventScroll:true});
};
const tkClose=()=>{
if(!tkSel)return;
pickBtn(tkSel)?.setAttribute("aria-expanded","false");
tkPanel.classList.add("hide");tkSel=null;tkCur=-1;tkAct("");
};
const tkChoose=v=>{
const sel=tkSel;if(!sel)return;
tkClose();
if(sel.value===v&&v!=="__custom")return;
sel.value=v;
sel.dispatchEvent(new Event("change",{bubbles:true}));
};
for(const sel of [fromSel,toSel]){
const b=pickBtn(sel);
if(b)b.addEventListener("click",e=>{e.stopPropagation();tkSel===sel?tkClose():tkOpen(sel)});
}
tkList.addEventListener("click",e=>{
const r=e.target.closest(".tkr");
if(!r||r.getAttribute("aria-disabled")==="true")return;
tkChoose(r.dataset.value);
});
tkFind.addEventListener("input",tkRows);
tkPanel.addEventListener("keydown",e=>{
const rows=[...tkList.querySelectorAll('.tkr:not([aria-disabled="true"])')];
if(e.key==="Escape"){e.preventDefault();const s=tkSel;tkClose();pickBtn(s)?.focus();return}
if(e.key==="ArrowDown"||e.key==="ArrowUp"){
e.preventDefault();
if(!rows.length)return;
tkCur=e.key==="ArrowDown"?(tkCur+1)%rows.length:(tkCur<=0?rows.length:tkCur)-1;
rows.forEach((r,i)=>r.classList.toggle("cur",i===tkCur));
rows[tkCur].scrollIntoView({block:"nearest"});
tkAct(rows[tkCur].id);
return;
}
if(e.key==="Enter"){
const r=tkCur>=0?rows[tkCur]:rows.length===1?rows[0]:null;
if(r){e.preventDefault();r.dataset.value?tkChoose(r.dataset.value):r.click()}
}
});
tkPanel.addEventListener("click",e=>e.stopPropagation());
document.addEventListener("click",tkClose);
addEventListener("resize",()=>{if(!matchMedia("(pointer:coarse)").matches)tkClose()});
addEventListener("scroll",e=>{if(tkSel&&!tkPanel.contains(e.target))tkClose()},true);
const render=()=>{
if(!account){swap.textContent="Connect Wallet";swap.disabled=false;return}
if(tab==="book"){
rate.textContent="";
const f=TOKENS[fromSel.value],t=TOKENS[toSel.value];
if(!f||!t){swap.textContent="Place order";swap.disabled=true;return}
if(f.std==="nft"&&t.std==="nft"){
swap.textContent="One side must be a token";swap.disabled=true;return}
if(t.std==="nft"){
const oneId=nftIdIn(t.addr,nftId.value);
if(oneId&&!/^\d+$/.test(oneId)){
swap.textContent=isWns(t.addr)?"Token id must be a number, or a .wei name":"Token id must be a number";
swap.disabled=true;return}
const climbing=kind.value==="floor";
if(kind.value==="dutch"){
swap.textContent="A bid is fixed or climbing, not decaying";swap.disabled=true;return}
if(oneId&&climbing){
swap.textContent="A climbing bid takes any id — clear the token id";
swap.disabled=true;return}
if(!oneId){
const q=outAmt.value.trim();
if(q&&!/^\d+$/.test(q)){
swap.textContent="How many? a whole number";swap.disabled=true;return}
if(q&&!BigInt(q)){swap.textContent="Bid for at least one";swap.disabled=true;return}
if(q&&BigInt(q)>(1n<<128n)-1n){
swap.textContent="Too many to bid for at once";swap.disabled=true;return}
}
let bidAmt=0n;
try{bidAmt=amt.value.trim()?parseUnits(amt.value.trim(),f.dec):0n}catch{}
if(!bidAmt){
swap.textContent=climbing?"Set the most you will pay":"Set what you will pay";
swap.disabled=true;return}
if(climbing){
let openAmt=0n;
try{openAmt=floorAmt.value.trim()?parseUnits(floorAmt.value.trim(),f.dec):0n}catch{}
if(!openAmt){swap.textContent="Set an opening bid";swap.disabled=true;return}
if(openAmt>bidAmt){swap.textContent="Opening bid exceeds the maximum";swap.disabled=true;return}
if(!+dly.value){swap.textContent="A climbing bid needs a window";swap.disabled=true;return}
}else if(!oneId&&!+dly.value){
swap.textContent="A collection bid needs a window";swap.disabled=true;return}
if(bidAmt>fromBalance){swap.textContent="Insufficient balance";swap.disabled=true;return}
swap.textContent=oneId?`Bid for ${nftLabel(t.sym,t.addr,nftId.value)}`
:climbing?`Climbing bid for any ${t.sym}`:`Bid for any ${t.sym}`;
swap.disabled=false;return}
if(f.std==="nft"){
if(kind.value!=="fixed"){
swap.textContent="An NFT sells at a fixed price";swap.disabled=true;return}
if(!/^\d+$/.test(nftIdIn(f.addr,nftId.value))){
swap.textContent=isWns(f.addr)?"Name the .wei name, or the token id, to sell":"Name the token id to sell";
swap.disabled=true;return}
let ask=0n;
try{ask=outAmt.value.trim()?parseUnits(outAmt.value.trim(),t.dec):0n}catch{}
if(!ask){swap.textContent="Set a price";swap.disabled=true;return}
swap.textContent=`List ${nftLabel(f.sym,f.addr,nftId.value)}`;swap.disabled=false;return}
const dutch=kind.value==="dutch",bid=kind.value==="floor";
let sell=0n,want=0n,floor=0n,badOrderAmount=false;
try{if(amt.value.trim())sell=parseUnits(amt.value.trim(),f.dec);
if(outAmt.value.trim())want=parseUnits(outAmt.value.trim(),t.dec);
if((dutch||bid)&&floorAmt.value.trim())floor=parseUnits(floorAmt.value.trim(),bid?f.dec:t.dec)}
catch{badOrderAmount=true}
if(badOrderAmount){swap.textContent="Invalid order amount";swap.disabled=true;return}
if(!sell||!want){swap.textContent=bid?"Place bid":"Place order";swap.disabled=true;return}
if(bid){
if(t.addr===ZERO){swap.textContent="Pick a token to bid for";swap.disabled=true;return}
if((f.addr===ZERO?WETH:f.addr).toLowerCase()===t.addr.toLowerCase()){
swap.textContent="Pick different token";swap.disabled=true;return}
if(!floor){swap.textContent="Set an opening bid";swap.disabled=true;return}
if(floor>sell){swap.textContent="Opening exceeds maximum";swap.disabled=true;return}
if(+dly.value===0){swap.textContent="Choose a bid window";swap.disabled=true;return}
if(sell>fromBalance){swap.textContent="Insufficient balance";swap.disabled=true;return}
swap.textContent=`Bid ${trimAmt(floor,f.dec)} → ${trimAmt(sell,f.dec)} ${f.sym} for ${trimAmt(want,t.dec)} ${t.sym}`;
swap.disabled=false;return;
}
if(f.addr===ZERO&&t.addr===WETH){swap.textContent="Pick different token";swap.disabled=true;return}
if(!dutch&&f.addr===WETH&&t.addr===ZERO){swap.textContent="Pick different token";swap.disabled=true;return}
if(dutch&&floor>want){swap.textContent="Floor exceeds start";swap.disabled=true;return}
if(dutch&&+dly.value===0){swap.textContent="Choose decay duration";swap.disabled=true;return}
if(sell>fromBalance){swap.textContent="Insufficient balance";swap.disabled=true;return}
swap.textContent=dutch
?`Dutch ${trimAmt(sell,f.dec)} ${f.sym} · ${trimAmt(want,t.dec)} → ${trimAmt(floor,t.dec)} ${t.sym}`
:`Place ${fill.value==="1"?"":"all-or-nothing "}order \u2014 ${trimAmt(sell,f.dec)} ${f.sym} \u2192 ${trimAmt(want,t.dec)} ${t.addr===ZERO?"WETH":t.sym}`;
{
const qs=t.addr===ZERO?"ETH":t.sym;
rate.textContent=dutch
?`Falls from ${trimAmt(want,t.dec)} to ${trimAmt(floor,t.dec)} ${qs} over ${rel(+dly.value)}, then rests at ${trimAmt(floor,t.dec)} ${qs} `
+(+dRest.value?`for ${rel(+dRest.value)} and returns to you`:`until you cancel`)
:`${fill.value==="1"?"Fills in any amount":"Fills all at once"} at ${trimAmt(want,t.dec)} ${t.addr===ZERO?"WETH":t.sym} for ${trimAmt(sell,f.dec)} ${f.sym}`
+(+dly.value?`, expiring in ${rel(+dly.value)}`:", and never expires");
}
swap.disabled=false;return;
}
if(tab==="send"){
if(!sendReady){
let n=0n;try{n=parseUnits(amt.value.trim(),TOKENS[fromSel.value].dec)}catch{}
swap.textContent=n>0n?"Name a recipient":"Enter an amount";
swap.disabled=true;return}
if(sendReady.amount>fromBalance){swap.textContent="Insufficient balance";swap.disabled=true;return}
const a=`${trimAmt(sendReady.amount,sendReady.token.dec)} ${sendReady.token.sym}`,dv=+dly.value;
if(dv&&sendTipWei>0n&&sendReady.token.addr===ZERO
&&sendReady.amount+sendTipWei>fromBalance){
swap.textContent="Insufficient ETH for amount + keeper tip";swap.disabled=true;return}
swap.textContent=(dv?`Lock ${a} for ${rel(dv)}`:`Send ${a}`)+` \u2192 ${sendReady.label}`;
swap.disabled=false;return;
}
if(quoting){swap.textContent="Quoting…";swap.disabled=true;return}
if(!last){swap.textContent="Swap";swap.disabled=true;return}
if(last.amountIn>fromBalance){swap.textContent="Insufficient balance";swap.disabled=true;return}
swap.textContent="Swap";swap.disabled=false;
};
syncIcons(); render();
const isRejection=e=>e?.code===4001||/user\s+reject|user\s+denied/i.test(e?.message||"");
const W6963=[];
addEventListener("eip6963:announceProvider",e=>{
const d=e.detail;
if(!d||!d.provider||!d.info||W6963.some(x=>x.info.uuid===d.info.uuid))return;
W6963.push(d)});
const wkScan=()=>{try{dispatchEvent(new Event("eip6963:requestProvider"))}catch{}};
wkScan();
const WC_PID="1e8390ef1c1d8a185e035912a1409749";
const WC_ICON='<svg viewBox="0 0 400 400" fill="none" aria-hidden="true"><circle cx="200" cy="200" r="200" fill="#3396ff"/><path fill="#fff" d="m122.519 148.965c42.791-41.729 112.171-41.729 154.962 0l5.15 5.022c2.14 2.086 2.14 5.469 0 7.555l-17.617 17.18c-1.07 1.043-2.804 1.043-3.874 0l-7.087-6.911