memoscan
← all memos

Memo 0x7937695f…99a34e on Ethereum

0xacfba7ce…4082·#25,829,676·view on Etherscan
="latest",I="eth_chainId"; const SCAN="https://etherscan.io/"; const FEE_BPS=250n,BPS=10000n; const PAGE=10; /* The bounty this page was built around. It is pinned, not privileged: it is read the same way as every other one, and nothing here can change it. */ const FEATURED=24n; /* The funder scan reads every bounty that has ever existed, so it is bounded - and the bound is shown to the reader rather than applied behind their back. At the observed rate (25 bounties in the contract's first 103 days) the cap is years away; the batch size keeps any one `aggregate3` a sane size. */ const SCAN_CAP=1000; /* How deep the account's own arrays are walked. They are public arrays with no length getter, so the end is found where the getter reverts. */ const PROBE=20; /* How far a bounty's own claim array is walked past the tenth. */ const CLAIM_CAP=100; const SEL_ETHBAL="4d2301cc"; /* Multicall3.getEthBalance(address) */ /* Selectors, written out rather than computed. The page carries a keccak for name resolution, but a constant that can be checked against the verified source by eye is better than one derived at run time. */ const R_COUNT="a0d59787",R_BOUNTY="dc2f8744",R_CLAIM="a888c2cd",R_BCLAIMS="34414835"; const R_PARTS="c1e3bd3e",R_PART="81fb1fb4",R_PEND="f3f43703",R_TRACK="10b385c2"; const R_CURVOTE="383d6d43",R_EXT="b04f5ebd",R_ROUND="2455a9c6"; const R_PERIOD="02a251a3",R_MINB="d5670525",R_MINC="40650c91",R_URI="c87b56dd"; const R_CLAIMSBY="b03a50c7"; /* Chainlink ETH/USD. The hosted UI takes its dollar figures from a price API; this is the same number with an onchain source, so the page keeps its one rule - it reads contracts, and nothing else. The feed is 8 decimals, and its own `updatedAt` is checked before the figure is shown, so a stalled feed is omitted rather than presented as a live price. */ const FEED="0x5f4ec3df9cbd43714fe2740f5e3616155c5b8419"; const F_ROUND="feaf968c"; const FEED_STALE=172800; const W_SOLO="d4ea4227",W_OPEN="8c1d93c5",W_JOIN="a9b2c6eb",W_WDOPEN="faf0f5e9"; const W_CSOLO="b1b4be97",W_COPEN="d496ec7a",W_REFUND="240b727e",W_MKCLAIM="83ffc9f2"; const W_ACCEPT="1d260f8e",W_SUBMIT="9e9251c8",W_VOTE="b7c8994d",W_RESOLVE="30f4ccac"; const W_WITHDRAW="3ccfd60b"; const R_UB="2b9234b9",R_UC="ca9748ea"; /* -------------------------------------------------------------- ABI I/O */ /* -------------------------------------------------------------- ABI I/O */ const strip0x=h=>String(h==null?"":h).replace(/^0x/,""); const pad32=h=>strip0x(h).padStart(64,"0"); const encUint=v=>BigInt(v).toString(16).padStart(64,"0"); const encAddr=a=>pad32(String(a).toLowerCase()); const encBool=b=>encUint(b?1:0); const encBytes=b=>{let h="";for(const x of b)h+=x.toString(16).padStart(2,"0"); return encUint(b.length)+(h?h.padEnd(Math.ceil(h.length/64)*64,"0"):"")}; const encStr=s=>encBytes(new TextEncoder().encode(s)); const word=(h,i)=>strip0x(h).slice(i*64,(i+1)*64); const uword=(h,i)=>{const w=word(h,i);return w?BigInt("0x"+w):0n}; const aword=(h,i)=>"0x"+word(h,i).slice(24); const hexNum=n=>"0x"+Number(n).toString(16); const decStrAt=(h,byteOff)=>{const s=strip0x(h),i=byteOff*2; const len=Number(BigInt("0x"+s.slice(i,i+64)));if(!len)return""; const raw=s.slice(i+64,i+64+len*2),b=new Uint8Array(len); for(let k=0;k<len;k++)b[k]=parseInt(raw.slice(k*2,k*2+2),16); return new TextDecoder().decode(b)}; const encAgg3=cs=>{let head="",tail="",off=cs.length*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(cs.length)+head+tail}; /* A read that came back as something other than an aggregate3 response - a truncated body, a provider answering an `eth_call` with a status word, a gateway returning a stub - used to surface as "Cannot convert 0x to a BigInt" from deep inside the decoder, which tells a reader nothing and hides which of their nodes is at fault. It is checked at the door instead. */ const decAgg3=hex=>{const h=strip0x(hex),at=i=>h.slice(i*64,(i+1)*64); if(h.length<128||h.length%64)throw Error( "The read node gave a malformed answer to a batched call. Pick another node with the read-node control."); 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))); out.push(ok?"0x"+h.slice((lw+1)*64,(lw+1)*64+len*2):null)} return out}; const mc3=async cs=>cs.length?decAgg3(await rpcRead(C,[{to:MC3,data:encAgg3(cs)},L])):[]; /* ------------------------------------------------------------- NUMBERS */ const fmt=(v,dp=4,dec=18)=>{ if(v==null)return"—"; v=BigInt(v);const neg=v<0n;if(neg)v=-v; const base=10n**BigInt(dec); let s=(v/base).toString(); let frac=(v%base).toString().padStart(dec,"0").slice(0,dp).replace(/0+$/,""); if(v>0n&&v<base&&!frac){dp=Math.min(dec,8);frac=(v%base).toString().padStart(dec,"0").slice(0,dp).replace(/0+$/,"")} s=s.replace(/\B(?=(\d{3})+(?!\d))/g,","); return (neg?"-":"")+s+(frac?"."+frac:"")}; const parseAmt=(t,dec=18)=>{ t=String(t||"").trim().replace(/,/g,""); if(!/^\d*\.?\d*$/.test(t)||t===""||t===".")return null; const[i,f=""]=t.split("."); if(f.length>dec)return null; return BigInt((i||"0")+f.padEnd(dec,"0"))}; const shortAddr=a=>a?a.slice(0,6)+"…"+a.slice(-4):""; const isAddr=a=>/^0x[0-9a-fA-F]{40}$/.test(String(a||"").trim()); const ago=t=>{ if(!t)return""; const s=Math.max(0,Math.floor(Date.now()/1000)-t); if(s<90)return"just now"; if(s<5400)return Math.round(s/60)+"m ago"; if(s<129600)return Math.round(s/3600)+"h ago"; return Math.round(s/86400)+"d ago"}; /* ------------------------------------------------- WALLETCONNECT (PORTED) Lifted verbatim from zSwap v0.2: the ChaCha20-Poly1305 / X25519 / Ed25519 helpers, the relay client, and a QR encoder - about 16 KB of protocol, carried so the page need not fetch a 635 KB bundle from a host at run time. A page that can never be updated cannot depend on a CDN still being there. It is self-contained: it touches no DOM and no state of this page, which is what made it portable at all. */ 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.911c-29.853-29.111-78.253-29.111-108.106 0l-7.59 7.401c-1.07 1.043-2.804 1.043-3.874 0l-17.617-17.18c-2.14-2.086-2.14-5.469 0-7.555zm191.397 35.529 15.679 15.29c2.14 2.086 2.14 5.469 0 7.555l-70.7 68.944c-2.139 2.087-5.608 2.087-7.748 0l-50.178-48.931c-.535-.522-1.402-.522-1.937 0l-50.178 48.931c-2.139 2.087-5.608 2.087-7.748 0l-70.7015-68.945c-2.1396-2.086-2.1396-5.469 0-7.555l15.6795-15.29c2.1396-2.087 5.6085-2.087 7.7481 0l50.1789 48.932c.535.522 1.402.522 1.937 0l50.177-48.932c2.139-2.087 5.608-2.087 7.748 0l50.179 48.932c.535.522 1.402.522 1.937 0l50.179-48.931c2.139-2.087 5.608-2.087 7.748 0z"/></svg>'; const WCU=(()=>{ const rl=(v,c)=>(v<<c|v>>>(32-c))>>>0; const wds=b=>{const o=[];for(let i=0;i<b.length;i+=4)o.push((b[i]|b[i+1]<<8|b[i+2]<<16|b[i+3]<<24)>>>0);return o}; function cblk(x,k,n,c){ const s=[0x61707865,0x3320646e,0x79622d32,0x6b206574, k[0],k[1],k[2],k[3],k[4],k[5],k[6],k[7],c,n[0],n[1],n[2]]; for(let i=0;i<16;i++)x[i]=s[i]; for(let r=0;r<10;r++){ const q=(a,b,cc,d)=>{ x[a]=(x[a]+x[b])>>>0;x[d]=rl(x[d]^x[a],16); x[cc]=(x[cc]+x[d])>>>0;x[b]=rl(x[b]^x[cc],12); x[a]=(x[a]+x[b])>>>0;x[d]=rl(x[d]^x[a],8); x[cc]=(x[cc]+x[d])>>>0;x[b]=rl(x[b]^x[cc],7)}; q(0,4,8,12);q(1,5,9,13);q(2,6,10,14);q(3,7,11,15); q(0,5,10,15);q(1,6,11,12);q(2,7,8,13);q(3,4,9,14);} for(let i=0;i<16;i++)x[i]=(x[i]+s[i])>>>0;} function chacha(key,nonce,ctr,src){ const k=wds(key),n=wds(nonce),x=new Uint32Array(16),out=new Uint8Array(src.length); for(let p=0;p<src.length;p+=64){cblk(x,k,n,ctr++); for(let i=0;i<64&&p+i<src.length;i++)out[p+i]=src[p+i]^(x[i>>2]>>>((i&3)*8)&255);} return out} function poly(msg,key){ const P=(1n<<130n)-5n; let r=0n,sk=0n; for(let i=15;i>=0;i--)r=(r<<8n)|BigInt(key[i]); r&=0x0ffffffc0ffffffc0ffffffc0fffffffn; for(let i=31;i>=16;i--)sk=(sk<<8n)|BigInt(key[i]); let a=0n; for(let i=0;i<msg.length;i+=16){ const n=Math.min(16,msg.length-i); let b=0n; for(let j=n-1;j>=0;j--)b=(b<<8n)|BigInt(msg[i+j]); a=((a+b+(1n<<BigInt(8*n)))*r)%P;} a=(a+sk)&((1n<<128n)-1n); const o=new Uint8Array(16); for(let i=0;i<16;i++){o[i]=Number(a&255n);a>>=8n} return o} const p16=n=>n%16?16-n%16:0; const le64=n=>{const b=new Uint8Array(8);for(let i=0;i<8;i++){b[i]=n&255;n=Math.floor(n/256)}return b}; function tagOf(ct,key,nonce){ const otk=chacha(key,nonce,0,new Uint8Array(32)); const pad=new Uint8Array(p16(ct.length)); const m=new Uint8Array(ct.length+pad.length+16); m.set(ct);m.set(pad,ct.length); m.set(le64(0),ct.length+pad.length);m.set(le64(ct.length),ct.length+pad.length+8); return poly(m,otk)} const seal=(key,nonce,pt)=>{ const ct=chacha(key,nonce,1,pt),tg=tagOf(ct,key,nonce); const o=new Uint8Array(ct.length+16);o.set(ct);o.set(tg,ct.length);return o}; const open=(key,nonce,box)=>{ if(box.length<16)return null; const ct=box.subarray(0,box.length-16),tg=box.subarray(box.length-16); const w=tagOf(ct,key,nonce); let bad=0;for(let i=0;i<16;i++)bad|=w[i]^tg[i]; return bad?null:chacha(key,nonce,1,ct)}; const hex=b=>{let s="";for(const x of b)s+=x.toString(16).padStart(2,"0");return s}; const unhex=h=>{const b=new Uint8Array(h.length/2); for(let i=0;i<b.length;i++)b[i]=parseInt(h.substr(i*2,2),16);return b}; const bin=b=>{let s="";for(let i=0;i<b.length;i+=4096)s+=String.fromCharCode.apply(null,b.subarray(i,i+4096));return s}; const b64=b=>btoa(bin(b)); const unb64=s=>{const t=atob(s),b=new Uint8Array(t.length); for(let i=0;i<t.length;i++)b[i]=t.charCodeAt(i);return b}; const b64u=b=>b64(b).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,""); const utf8=s=>new TextEncoder().encode(s); const B58="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; const b58=b=>{let n=0n;for(const x of b)n=n*256n+BigInt(x); let s="";while(n>0n){s=B58[Number(n%58n)]+s;n/=58n} for(const x of b){if(x)break;s="1"+s}return s}; const rnd=n=>crypto.getRandomValues(new Uint8Array(n)); const sha256=async b=>new Uint8Array(await crypto.subtle.digest("SHA-256",b)); const topicOf=async k=>hex(await sha256(k)); const encode=(symKey,payload,senderPub)=>{ const iv=rnd(12),pt=utf8(JSON.stringify(payload)); const sealed=seal(symKey,iv,pt); const t=senderPub?1:0; const head=senderPub?1+32:1; const out=new Uint8Array(head+12+sealed.length); out[0]=t; if(senderPub)out.set(senderPub,1); out.set(iv,head);out.set(sealed,head+12); return b64(out)}; const decode=(symKey,msg)=>{ const b=unb64(msg); if(!b.length)return null; const t=b[0]; if(t!==0&&t!==1)return null; const head=t===1?33:1; if(b.length<head+12+16)return null; const iv=b.subarray(head,head+12),box=b.subarray(head+12); const pt=open(symKey,iv,box); if(!pt)return null; try{return JSON.parse(new TextDecoder().decode(pt))}catch{return null}}; const didKey=pub=>{const b=new Uint8Array(34);b[0]=0xed;b[1]=0x01;b.set(pub,2); return"did:key:z"+b58(b)}; async function relayJwt(aud){ let kp; try{kp=await crypto.subtle.generateKey({name:"Ed25519"},true,["sign","verify"])} catch{throw Error("This browser cannot do Ed25519. WalletConnect needs a newer " +"browser; an extension wallet works here today.")} const pub=new Uint8Array(await crypto.subtle.exportKey("raw",kp.publicKey)); const iss=didKey(pub); const iat=Math.floor(Date.now()/1e3); const h=b64u(utf8(JSON.stringify({alg:"EdDSA",typ:"JWT"}))); const p=b64u(utf8(JSON.stringify({iss,sub:hex(rnd(32)),aud,iat,exp:iat+86400}))); const sig=new Uint8Array(await crypto.subtle.sign({name:"Ed25519"},kp.privateKey,utf8(h+"."+p))); return h+"."+p+"."+b64u(sig)} async function kex(){ let kp; try{kp=await crypto.subtle.generateKey({name:"X25519"},true,["deriveBits"])} catch{throw Error("This browser cannot do X25519. WalletConnect needs a newer " +"browser; an extension wallet works here today.")} const pub=new Uint8Array(await crypto.subtle.exportKey("raw",kp.publicKey)); return{priv:kp.privateKey,pub}} async function sessionKey(priv,peerHex){ const peer=await crypto.subtle.importKey("raw",unhex(peerHex),{name:"X25519"},false,[]); const bits=new Uint8Array(await crypto.subtle.deriveBits({name:"X25519",public:peer},priv,256)); const hk=await crypto.subtle.importKey("raw",bits,"HKDF",false,["deriveBits"]); const out=await crypto.subtle.deriveBits( {name:"HKDF",hash:"SHA-256",salt:new Uint8Array(0),info:new Uint8Array(0)},hk,256); return new Uint8Array(out)} return{seal,open,chacha,poly,hex,unhex,b64,unb64,b64u,b58,didKey,rnd,sha256, topicOf,encode,decode,relayJwt,kex,sessionKey}; })(); const WCCLI=(U,WSC)=>{ const RELAY="wss://relay.walletconnect.org"; const T_PROPOSE=1100,T_APPROVE=1101,T_SETTLE=1102,T_SETTLE_RES=1103,T_REQ=1108,T_RES=1109; const TTL=300,STTL=604800; let sock=null,nextId=1,waiters={},subs={},onEvent=null,alive=false,gone=""; const rpcId=()=>Date.now()*1000+(nextId++%1000); const sendRaw=o=>{ if(!alive)throw Error(gone||"The wallet connection is closed."); sock.send(JSON.stringify(o))}; function drop(why){ if(!alive&&gone)return; alive=false;gone=why; const ws=waiters;waiters={};subs={}; for(const k in ws)ws[k].rej(Error(why)); if(onEvent)onEvent({t:"closed",why})} function relayCall(method,params){ const id=rpcId(); return new Promise((res,rej)=>{ waiters[id]={res,rej}; sendRaw({id,jsonrpc:"2.0",method,params}); setTimeout(()=>{if(waiters[id]){delete waiters[id];rej(Error("relay timeout"))}},15000)})} function handle(raw){ let m;try{m=JSON.parse(raw)}catch{return} if(m.method==="irn_subscription"){ sendRaw({id:m.id,jsonrpc:"2.0",result:true}); const d=m.params&&m.params.data; if(d&&subs[d.topic])subs[d.topic](d.message); return} if(m.id&&waiters[m.id]){ const w=waiters[m.id];delete waiters[m.id]; if(m.error)w.rej(Error(m.error.message||"relay error"));else w.res(m.result)}} async function connect(projectId){ try{sock&&sock.close()}catch{} sock=null;alive=false;gone="";waiters={};subs={}; const jwt=await U.relayJwt(RELAY); const url=`${RELAY}/?auth=${jwt}&projectId=${projectId}&ua=zswap`; const ws=WSC(url); sock=ws; await new Promise((res,rej)=>{ let d=0; const t=setTimeout(()=>{if(!d){d=1;rej(Error("The relay did not answer."))}},15000); const fail=m=>{try{ws.close()}catch{}if(sock===ws)sock=null;rej(Error(m))}; const end=f=>x=>{if(d)return;d=1;clearTimeout(t);f(x)}; sock.onopen=end(()=>{alive=true;res()}); sock.onerror=end(()=>fail("The relay could not be reached.")); sock.onclose=end(e=>fail("The relay closed the connection" +(e&&e.code?" (code "+e.code+(e.reason?": "+e.reason:"")+")":"")+"."))}) sock.onclose=()=>drop("The relay connection dropped. Reconnect your wallet."); sock.onmessage=e=>handle(typeof e.data==="string"?e.data:""); } const subscribe=async(topic,fn)=>{subs[topic]=fn;await relayCall("irn_subscribe",{topic})}; const publish=(topic,message,tag,ttl)=> relayCall("irn_publish",{topic,message,ttl:ttl||TTL,tag,prompt:tag===T_PROPOSE||tag===T_REQ}); async function pair(projectId,meta){ await connect(projectId); const symKey=U.rnd(32); const pTopic=await U.topicOf(symKey); const kp=await U.kex(); const proposeId=rpcId(); let settle=null; const done=new Promise((res,rej)=>{settle={res,rej}}); await subscribe(pTopic,async msg=>{ const m=U.decode(symKey,msg); if(!m||m.id!==proposeId)return; if(m.error)return settle.rej(Error(m.error.message||"rejected")); const rp=m.result&&m.result.responderPublicKey; if(!rp)return; const sKey=await U.sessionKey(kp.priv,rp); const sTopic=await U.topicOf(sKey); await subscribe(sTopic,async smsg=>{ const s=U.decode(sKey,smsg); if(!s)return; if(s.method==="wc_sessionSettle"){ await publish(sTopic,U.encode(sKey,{id:s.id,jsonrpc:"2.0",result:true}),T_SETTLE_RES,STTL); const ns=s.params&&s.params.namespaces||{}; const accts=[]; for(const k in ns)for(const a of ns[k].accounts||[])accts.push(a); settle.res({topic:sTopic,key:sKey,accounts:accts, methods:(ns.eip155&&ns.eip155.methods)||[]}); return} if(s.method==="wc_sessionDelete")return drop("The wallet ended the session."); if(s.method&&onEvent)onEvent({t:"session",m:s}); if(s.id&&waiters["s"+s.id]){ const w=waiters["s"+s.id];delete waiters["s"+s.id]; if(s.error)w.rej(Object.assign(Error(s.error.message||"rejected"),{code:s.error.code})); else w.res(s.result)}}); }); const proposal={id:proposeId,jsonrpc:"2.0",method:"wc_sessionPropose",params:{ relays:[{protocol:"irn"}], proposer:{publicKey:U.hex(kp.pub),metadata:meta}, requiredNamespaces:{}, optionalNamespaces:{eip155:{chains:["eip155:1"], methods:["eth_sendTransaction","personal_sign","eth_signTypedData_v4", "eth_call","eth_chainId","eth_accounts","eth_getBalance", "wallet_switchEthereumChain"], events:["chainChanged","accountsChanged"]}}}}; await publish(pTopic,U.encode(symKey,proposal),T_PROPOSE); const uri=`wc:${pTopic}@2?relay-protocol=irn&symKey=${U.hex(symKey)}`; return{uri,done}} function request(sess,method,params){ const id=rpcId(); const body={id,jsonrpc:"2.0",method:"wc_sessionRequest", params:{request:{method,params:params||[]},chainId:"eip155:1"}}; const p=new Promise((res,rej)=>{ waiters["s"+id]={res,rej}; setTimeout(()=>{if(waiters["s"+id]){delete waiters["s"+id];rej(Error("wallet timed out"))}},300000)}); publish(sess.topic,U.encode(sess.key,body),T_REQ).catch(e=>{ const w=waiters["s"+id];if(!w)return;delete waiters["s"+id];w.rej(e)}); return p} return{pair,request,close:()=>{drop("The wallet connection was closed.");try{sock&&sock.close()}catch{}sock=null}, on:f=>{onEvent=f},_relayCall:relayCall}; }; const QR8=(()=>{ const EXP=new Uint8Array(512),LOG=new Uint8Array(256); (()=>{let x=1;for(let i=0;i<255;i++){EXP[i]=x;LOG[x]=i;x<<=1;if(x&256)x^=285} for(let i=255;i<512;i++)EXP[i]=EXP[i-255]})(); const mul=(a,b)=>a&&b?EXP[LOG[a]+LOG[b]]:0; const gen=n=>{let p=[1]; for(let i=0;i<n;i++){const q=new Array(p.length+1).fill(0); for(let j=0;j<p.length;j++){q[j]^=mul(p[j],EXP[i]);q[j+1]^=p[j]}p=q} return p}; const rs=(data,n)=>{ const g=gen(n),r=new Uint8Array(data.length+n); r.set(data); for(let i=0;i<data.length;i++){ const c=r[i];if(!c)continue; for(let j=0;j<=n;j++)r[i+j]^=mul(g[n-j],c)} return r.subarray(data.length)}; const SZ=49,NBLK=2,DPB=97,ECB=24,ALIGN=[6,24,42]; const bch=(v,g,n)=>{let d=v<<n; const bits=x=>{let c=0;while(x){c++;x>>>=1}return c}; const gl=bits(g); while(bits(d)>=gl)d^=g<<(bits(d)-gl); return d}; const fmtBits=mask=>{ const v=(1<<3)|mask; return ((v<<10)|bch(v,0x537,10))^0x5412}; const verBits=()=>(8<<12)|bch(8,0x1f25,12); function encode(str){ const bytes=new TextEncoder().encode(str); if(bytes.length>NBLK*DPB-2)throw Error("pairing code too long to draw"); const bits=[]; const push=(v,n)=>{for(let i=n-1;i>=0;i--)bits.push((v>>i)&1)}; push(4,4);push(bytes.length,8); for(const b of bytes)push(b,8); const cap=(NBLK*DPB)*8; for(let i=0;i<4&&bits.length<cap;i++)bits.push(0); while(bits.length%8)bits.push(0); const cw=[]; for(let i=0;i<bits.length;i+=8){let v=0;for(let j=0;j<8;j++)v=(v<<1)|bits[i+j];cw.push(v)} const pads=[0xec,0x11]; for(let i=0;cw.length<NBLK*DPB;i++)cw.push(pads[i%2]); const blocks=[],ecs=[]; for(let i=0;i<NBLK;i++){ const d=Uint8Array.from(cw.slice(i*DPB,(i+1)*DPB)); blocks.push(d);ecs.push(rs(d,ECB))} const out=[]; for(let i=0;i<DPB;i++)for(const b of blocks)out.push(b[i]); for(let i=0;i<ECB;i++)for(const e of ecs)out.push(e[i]); return out} function frame(){ const m=[],fn=[]; for(let i=0;i<SZ;i++){m.push(new Array(SZ).fill(0));fn.push(new Array(SZ).fill(0))} const put=(r,c,v)=>{m[r][c]=v;fn[r][c]=1}; const finder=(r,c)=>{ for(let i=-1;i<8;i++)for(let j=-1;j<8;j++){ const rr=r+i,cc=c+j; if(rr<0||cc<0||rr>=SZ||cc>=SZ)continue; const on=(i>=0&&i<=6&&(j===0||j===6))||(j>=0&&j<=6&&(i===0||i===6))||(i>=2&&i<=4&&j>=2&&j<=4); put(rr,cc,on?1:0)}}; finder(0,0);finder(0,SZ-7);finder(SZ-7,0); for(const r of ALIGN)for(const c of ALIGN){ if((r===6&&c===6)||(r===6&&c===ALIGN[ALIGN.length-1])||(r===ALIGN[ALIGN.length-1]&&c===6))continue; for(let i=-2;i<=2;i++)for(let j=-2;j<=2;j++) put(r+i,c+j,(Math.abs(i)===2||Math.abs(j)===2||(i===0&&j===0))?1:0)} for(let i=8;i<SZ-8;i++){put(6,i,i%2?0:1);put(i,6,i%2?0:1)} put(SZ-8,8,1); for(let i=0;i<9;i++){if(!fn[8][i])put(8,i,0);if(!fn[i][8])put(i,8,0)} for(let i=0;i<8;i++){if(!fn[8][SZ-1-i])put(8,SZ-1-i,0);if(!fn[SZ-1-i][8])put(SZ-1-i,8,0)} const vb=verBits(); for(let i=0;i<18;i++){ const b=(vb>>i)&1,r=Math.floor(i/3),c=i%3; put(SZ-11+c,r,b);put(r,SZ-11+c,b)} return{m,fn}} function place(m,fn,cw){ let bit=0,up=1; const get=i=>(cw[i>>3]>>(7-(i&7)))&1; for(let c=SZ-1;c>0;c-=2){ if(c===6)c--; for(let k=0;k<SZ;k++){ const r=up?SZ-1-k:k; for(let j=0;j<2;j++){ const cc=c-j; if(fn[r][cc])continue; m[r][cc]=bit<cw.length*8?get(bit):0;bit++}} up^=1}} const maskFn=(k,r,c)=>[ (r+c)%2===0,r%2===0,c%3===0,(r+c)%3===0, (((r/2)|0)+((c/3)|0))%2===0,((r*c)%2+(r*c)%3)===0, (((r*c)%2+(r*c)%3)%2)===0,(((r+c)%2+(r*c)%3)%2)===0][k]; function penalty(m){ let p=0; for(let i=0;i<SZ;i++){ let rc=1,cc=1; for(let j=1;j<SZ;j++){ rc=m[i][j]===m[i][j-1]?rc+1:1;if(rc===5)p+=3;else if(rc>5)p++; cc=m[j][i]===m[j-1][i]?cc+1:1;if(cc===5)p+=3;else if(cc>5)p++}} for(let i=0;i<SZ-1;i++)for(let j=0;j<SZ-1;j++){ const s=m[i][j]+m[i][j+1]+m[i+1][j]+m[i+1][j+1]; if(s===0||s===4)p+=3} const pat=[1,0,1,1,1,0,1]; const hit=(get)=>{ for(let i=0;i<SZ;i++)for(let j=0;j<SZ-6;j++){ let okk=1;for(let k=0;k<7;k++)if(get(i,j+k)!==pat[k]){okk=0;break} if(!okk)continue; let before=1,after=1; for(let k=j-4;k<j;k++)if(k<0||get(i,k)