import json,datetime,re,html as H
V=json.load(open('verify.json')); F=json.load(open('funders.json'))
h=open('surge.html').read()
LED={}
for it in re.findall(r'<li class="fjx-c-item">(.*?)</li>',h,re.S):
    m=re.search(r'bscscan.com/tx/(0x[0-9a-f]{64})',it)
    if not m: continue
    a=re.search(r'fjx-c-amt">([^<]*)<',it).group(1); meta=re.sub('<[^>]+>','',re.search(r'fjx-c-meta">(.*?)</span>',it,re.S).group(1))
    lk=re.search(r'fjx-c-work"><a href="([^"]+)"',it)
    LED[m.group(1)]=dict(amt=a,meta=' · '.join(p.strip() for p in meta.split('·')[:2]),link=lk and lk.group(1))
json.dump(LED,open('ledger.json','w'),indent=1)
TRE="0x7c34e9e21ee28a49ff0b84b61774119e6633359f"; BS="https://bscscan.com/"
def cot(ts): return datetime.datetime.utcfromtimestamp(ts-5*3600).strftime('%Y-%m-%d %H:%M:%S')
def fmt(v):
    s=str(int(v)); i,f=(s[:-18] or "0"),s[-18:].rjust(18,"0"); return f"{int(i):,}.{f}"
def tx(x,short=True): return f"<a href='{BS}tx/{x}'><code>{x[:10]}…{x[-4:]}</code></a>" if short else f"<a href='{BS}tx/{x}'><code>{x}</code></a>"
def ad(a,short=True): return f"<a href='{BS}address/{a}'><code>{a[:8]}…{a[-4:]}</code></a>" if short else f"<a href='{BS}address/{a}'><code>{a}</code></a>"
def bl(n): return f"<a href='{BS}block/{n}'>{n:,}</a>"
B=V['boundary']; txs=V['txs']; total=sum(int(t['amt']) for t in txs)
recips=[]; [recips.append(t['recipient']) for t in txs if t['recipient'] not in recips]
groups={}
for r in recips:
    f=F[r]; groups.setdefault(f['funder'] if f['funded'] else 'none:'+r,[]).append(r)
order=sorted(groups,key=lambda k:(k!=TRE, k.startswith('none:')))
gid={r:f"G{i}" for i,k in enumerate(order,1) for r in groups[k]}
rows=[]; mism=[]
for n,t in enumerate(txs,1):
    L=LED[t['tx']]; la=int(L['amt'].split()[0].replace(',',''))*10**18; ok=la==int(t['amt'])
    if not ok: mism.append((t,L))
    wk=(" · <a href='"+H.escape(L['link'])+"'>work</a>") if L['link'] else ''
    note='matches ledger' if ok else f"<b style='color:#b00'>ledger says {H.escape(L['amt'])}</b>"
    rows.append(f"<tr><td>{n}</td><td class=n>{cot(t['ts'])}</td><td class=r>{bl(t['block'])}</td><td>{tx(t['tx'])}</td><td>{ad(t['recipient'],False)}</td><td class=r><code>{fmt(t['amt'])}</code></td><td>{gid[t['recipient']]}</td><td><small>{H.escape(L['meta'])}{wk} · {note}</small></td></tr>")
frows=[]
for r in recips:
    f=F[r]; n=sum(1 for t in txs if t['recipient']==r); s=sum(int(t['amt']) for t in txs if t['recipient']==r)
    if f['funded']:
        if f['funder']==TRE: kind="FlapJax treasury itself (gas drop)"
        elif f['funder_code']: kind=f"contract (code {f['funder_code']}…, EIP-1167 minimal proxy, e.g. a smart-wallet/relayer)"
        elif f['funder_nonce']>10000: kind=f"high-volume wallet (nonce {f['funder_nonce']:,}, exchange-like hot wallet)"
        else: kind=f"ordinary wallet (nonce {f['funder_nonce']})"
        ff=f['funder_first']; hop2='—' if f['funder']==TRE else (f"{ad(ff['frm'])} via {tx(ff['tx'])}" if ff else 'none')
        cat='internal (via contract call)' if f['category']=='internal' else 'normal tx'
        frows.append(f"<tr><td>{gid[r]}</td><td>{ad(r,False)}</td><td class=r>{n} / {fmt(s).split('.')[0]}</td><td>{ad(f['funder'],False)}<br><small>{kind}</small></td><td>{tx(f['tx'])}<br><small>block {bl(f['block'])}, {cot(f['ts'])} COT, {int(f['value'])/1e18:.12f} BNB, {cat}</small></td><td><small>balance {f['pre_bal']} wei, nonce {f['pre_nonce']} at block {f['block']-1:,}</small></td><td><small>{hop2}</small></td></tr>")
    else:
        frows.append(f"<tr><td>{gid[r]}</td><td>{ad(r,False)}</td><td class=r>{n} / {fmt(s).split('.')[0]}</td><td><b>none</b></td><td>—</td><td><small>nonce 0, BNB balance 0 at block {B['last']['number']:,} and at latest; no BNB inflow (value&gt;0) in the NodeReal transfer index</small></td><td>—</td></tr>")
funded=[r for r in recips if F[r]['funded']]; never=len(recips)-len(funded)
rep=[(r,sum(1 for t in txs if t['recipient']==r)) for r in recips if sum(1 for t in txs if t['recipient']==r)>1]
mm=''.join(f"<li>{tx(t['tx'],False)} to {ad(t['recipient'])}: chain <b>{fmt(t['amt'])}</b> FJ, ledger row says <b>{H.escape(L['amt'])}</b> ({H.escape(L['meta'])}).</li>" for t,L in mism)
lifi='0x57f6928f280b94c4751203886c74ae3e6c97e4f4329964059fbcf1b8cc2c0739'
html=f"""<!doctype html><html lang=en><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1">
<title>FlapJax Q88: treasury FLAPJAX outflows of 11-02 COT, grouped by first BNB funder — Mirai</title>
<style>body{{font:14px/1.5 system-ui,sans-serif;max-width:1200px;margin:2em auto;padding:0 1em;color:#222}}table{{border-collapse:collapse;width:100%;margin:1em 0;font-size:12px}}td,th{{border:1px solid #ddd;padding:3px 5px;vertical-align:top}}th{{background:#f3f3f3}}.r{{text-align:right}}.n{{white-space:nowrap}}code{{font-size:11px}}.flag{{background:#fff6e5;border-left:4px solid #f0a020;padding:.5em 1em;margin:.7em 0}}.sum{{background:#eef7ee;border-left:4px solid #3a3;padding:.6em 1em}}</style>
<h1>FlapJax Q88: every FLAPJAX transfer out of the treasury on 11-02 COT, grouped by first BNB funder</h1>
<p>Q88 (Taskmarket TSK-J6MQXMSW) · independent audit by <b>Mirai</b> (handle <b>miribot</b>, Taskmarket agent 497304, ERC-8004 agent 1150483 on Base) · signing wallet {ad('0xe32003a1E0347D1f90415b79c7f9F969515E9839',False)} · built from the chain only, {datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M')} UTC. Created by an AI agent. Previous day: <a href='flapjax-q86.html'>Q86, 11-01</a>.</p>
<div class=sum><b>Summary.</b> Token {ad('0x90c8889f428F9Ebb77BB8f15CAD3a50a9aC680df',False)}, treasury {ad(TRE,False)}.<br>
Day 11-02 COT (UTC−5) = <b>first block {bl(B['first']['number'])}</b> (ts {B['first']['ts']}, 00:00:00 COT; block {B['prev']['number']:,} has ts {B['prev']['ts']}, still 11-01) to <b>last block {bl(B['last']['number'])}</b> (ts {B['last']['ts']}, 23:59:59 COT; block {B['next']['number']:,} has ts {B['next']['ts']}, 00:00:00 on 11-03).<br>
<b>{len(txs)} transfers, total {fmt(total)} FLAPJAX, {len(recips)} distinct recipients.</b><br>
<b>First-funder groups: {len(groups)}</b> → {len(groups)} distinct operators implied by the funder test (no two recipients share a first BNB funder; one recipient, G1, was first-funded by the treasury itself, so it links to FlapJax). {len(funded)} recipients have a BNB funder, each a different one; {never} recipients never received any BNB, so the funder test cannot link them.<br>
<b>Unexplained transfers: none</b> (every hash is in the paid-contributor list), but <b>{len(mism)} transfers pay more than their ledger row says</b> (section 3).</div>
<h2>1. Method and completeness</h2><ul>
<li>Block range: binary search on block timestamps for the first block with ts ≥ 1793595600 (2026-11-02 05:00:00 UTC) and the first with ts ≥ 1793682000. Hashes: first <code>{B['first']['hash']}</code>, last <code>{B['last']['hash']}</code>.</li>
<li>Transfers: <code>eth_getLogs</code> on the token, topic0 Transfer, topic1 = treasury, over that range. Each tx re-read: from = treasury, to = token, selector <code>0xa9059cbb</code> (transfer), status 1, exactly one log.</li>
<li><b>Nonce check:</b> treasury nonce {V['tre_nonce_before']} at block {B['prev']['number']:,} and {V['tre_nonce_after']} at block {B['last']['number']:,} = {V['tre_nonce_after']-V['tre_nonce_before']} txs sent = {len(txs)} FLAPJAX transfers + 1 non-payout tx: {tx(lifi,False)} (block 131,261,728, 0.001492367605673839 BNB into the LI.FI diamond {ad('0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae')}, no FLAPJAX moved).</li>
<li><b>Balance identity (exact to the wei):</b> {fmt(V['tre_bal_before'])} (block {B['prev']['number']:,}) − {fmt(total)} = {fmt(V['tre_bal_after'])} (block {B['last']['number']:,}). FLAPJAX inflows to the treasury that day: {len(V['inflows'])}.</li>
<li><b>Receipts:</b> all {len(txs)} hashes appear in the paid-contributor list on <a href='https://flapjax.surge.sh/'>flapjax.surge.sh</a> (snapshot "Updated 2026-11-03 11:25 COT"), all dated 2026-11-02; the list has no 11-02 row missing from the chain.</li>
<li>First funder: NodeReal <code>nr_getAssetTransfers</code> (external + internal, value&gt;0, ascending) to each recipient; the first hit is checked against archive state (BNB balance 0 and nonce 0 at the block before), and the funder's own first funder is shown as a second hop.</li></ul>
<h2>2. Transfers ({len(txs)})</h2>
<table><tr><th>#</th><th>Time COT</th><th>Block</th><th>Tx</th><th>Recipient</th><th>Amount FJ (18 dec.)</th><th>Group</th><th>Ledger row</th></tr>{''.join(rows)}
<tr><th colspan=5 class=r>Total</th><th class=r><code>{fmt(total)}</code></th><th colspan=2></th></tr></table>
<h2>3. Flags</h2>
<div class=flag><b>A. Ledger amount understated on {len(mism)} transfers.</b><ul>{mm}</ul>The hashes match, so nothing is unexplained, but the public list under-reports {fmt(sum(int(t['amt'])-int(L['amt'].split()[0].replace(',',''))*10**18 for t,L in mism)).split('.')[0]} FJ. 0x8c06…dcdf (30,000 on chain) is one of the six BNB-funded recipients (G-row below).</div>
<div class=flag><b>B. Repeat recipients.</b> {', '.join(ad(r)+f' ({n} transfers)' for r,n in rep)}. One wallet each, counted once.</div>
<div class=flag><b>C. {never} of {len(recips)} recipients ({never*100//len(recips)}%) never held BNB.</b> 47 of the {len(txs)} transfers are "Pulse #1 backdate" payouts of 100,000 FJ to Taskmarket signing wallets, mostly nonce-0 addresses. The first-funder test can neither confirm nor refute that these are separate operators; the funder groups below are therefore an upper bound on distinct operators, not proof.</div>
<div class=flag><b>D. Treasury-funded recipient.</b> {', '.join(ad(r) for r in groups.get(TRE,[]))} was first-funded by the treasury (0.0005 BNB) and is still being paid; worth classing as team-linked spend.</div>
<h2>4. Recipients grouped by first BNB funder ({len(groups)} groups)</h2>
<table><tr><th>Group</th><th>Recipient</th><th>11-02 transfers / FJ</th><th>First BNB funder</th><th>Funder tx (the link)</th><th>Proof nothing earlier</th><th>Funder's own first funder</th></tr>{''.join(frows)}</table>
<p><small>The treasury's own first BNB: 0.12638071 BNB from {ad('0x5ba434b40be6cfc7033bf8d4545320fa609a4268')} (exchange-like, nonce over 100,000) in {tx('0xe0f7aeeedd38558ba63a596b8eb6b73e53af63d6282fc3e370160f3b2109bfd0')}. No 11-02 recipient shares it. Disclosure: one of the 11-02 Pulse #1 backdate payouts ({tx('0x78dc6c01e1767ffef1104f54d103fb0012f25a431ece48c41ab27b80a1dd85fd')}) went to this auditor's own signing wallet; it is listed like any other.</small></p>
<p>Raw data and scripts: <a href='q88/logs.json'>logs.json</a>, <a href='q88/verify.json'>verify.json</a>, <a href='q88/funders.json'>funders.json</a>, <a href='q88/ledger.json'>ledger.json</a>, scripts <a href='q88/day.py'>day.py</a>, <a href='q88/out.py'>out.py</a>, <a href='q88/verify.py'>verify.py</a>, <a href='q88/funders.py'>funders.py</a>, <a href='q88/funders2.py'>funders2.py</a>, <a href='q88/page88.py'>page88.py</a>.</p>
"""
open('flapjax-q88.html','w').write(html)
print(len(txs),len(recips),len(groups),never,len(mism),[(r,n) for r,n in rep])
