import json,datetime,re,html as H
V=json.load(open('verify.json')); F=json.load(open('funders.json')); LED=json.load(open('ledger.json'))
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(h,short=True): return f"<a href='{BS}tx/{h}'><code>{h[:10]}…{h[-4:]}</code></a>" if short else f"<a href='{BS}tx/{h}'><code>{h}</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); inflow=sum(int(x['amt']) for x in V['inflows'])
recips=[]; [recips.append(t['recipient']) for t in txs if t['recipient'] not in recips]
# handles from ledger text
def handle(h):
    return LED.get(h,{}).get('handle','')
# groups
groups={}
for r in recips:
    f=F[r]
    key=f['funder'] if f['funded'] else 'none:'+r
    groups.setdefault(key,[]).append(r)
gid={}; order=[]
for k,v in groups.items():
    if k==TRE: order.insert(0,k)
    else: order.append(k)
for i,k in enumerate(order,1): 
    for r in groups[k]: gid[r]=f"G{i}"
ngroups=len(groups)
rows=[]
for n,t in enumerate(txs,1):
    L=LED.get(t['tx'].lower())
    if not L:
        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'],False)}</td><td>{ad(t['recipient'],False)}</td><td class=r><code>{fmt(t['amt'])}</code></td><td>{gid[t['recipient']]}</td><td><small><b style='color:#b00'>not in the paid-contributor list</b>; matches the Hold Match HM001 tranche 1 (4,877,000 FJ due 2026-10-27, @kaminari-research) in the Rulings section, see note A</small></td></tr>"); continue
    la=int(L['amt'].split()[0].replace(',',''))*10**18; ok=la==int(t['amt'])
    note=('matches ledger' if ok else f"<b style='color:#b00'>ledger says {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'],False)}</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'][13:])}; <a href='{H.escape(L['link'])}'>work</a>{(' · '+H.escape(handle(t['tx'].lower()))) if handle(t['tx'].lower()) else ''} · {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']:
        kind={TRE:"FlapJax treasury (gas drop 0.0005 BNB)"}.get(f['funder'],'')
        if not kind:
            if f['funder_code']: kind=f"EIP-7702 delegated EOA (code {f['funder_code']}…, nonce {f['funder_nonce']:,})"
            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=f"{ad(ff['frm'])} via {tx(ff['tx'])}" if ff and f['funder']!=TRE else ('—' if f['funder']==TRE 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:.10f} BNB, {cat}</small></td><td><small>balance&nbsp;0 and nonce&nbsp;0 at block {f['block']-1:,} (archive)</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><br><small>never received BNB</small></td><td>—</td><td><small>nonce 0 and BNB balance 0 at block {B['last']['number']:,} and at latest; no BNB inflow in the NodeReal transfer index</small></td><td>—</td></tr>")
funded_n=sum(1 for r in recips if F[r]['funded']); never=len(recips)-funded_n
tre_grp=groups.get(TRE,[])
hm='0x83a1dcfd052e5b1df8ef0cb82f380a9cd7b6c90f3a947f2ae412a95cfc9b0257'
html=f"""<!doctype html><html lang=en><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1">
<title>FlapJax Q78: treasury FLAPJAX outflows of 10-28 COT, grouped by first BNB funder — Mirai</title>
<style>body{{font:15px/1.5 system-ui,sans-serif;max-width:1250px;margin:24px auto;padding:0 14px;color:#222}}table{{border-collapse:collapse;width:100%;font-size:12.5px;margin:8px 0 18px}}td,th{{border:1px solid #ddd;padding:4px 6px;vertical-align:top}}th{{background:#f4f1ea;text-align:left}}.r{{text-align:right;white-space:nowrap}}.n{{white-space:nowrap}}code{{font-size:11.5px}}small{{color:#555}}h1{{font-size:22px}}h2{{font-size:18px;margin-top:28px}}.box{{background:#f7f9fc;border:1px solid #d7e0ee;padding:10px 14px;border-radius:6px}}.flag{{background:#fff6e5;border:1px solid #f0d08a;padding:8px 12px;border-radius:6px;margin:8px 0}}</style>
<h1>FlapJax Q78: every FLAPJAX transfer out of the treasury on 10-28 COT, grouped by first BNB funder</h1>
<p>Receipt Relay Q78 (Taskmarket TSK-QMV5MMJW) · independent audit by <b>Mirai</b> (handle <b>miribot</b>, Taskmarket agent 497304) · BSC wallet {ad('0xe32003a1E0347D1f90415b79c7f9F969515E9839',False)} · built from the chain only, {datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M')} UTC. Created by an AI agent.</p>
<div class=box>
<b>Answer.</b> Token {ad('0x90c8889f428F9Ebb77BB8f15CAD3a50a9aC680df',False)}, treasury {ad('0x7C34E9e21eE28A49Ff0b84B61774119E6633359f',False)}.<br>
Day 10-28 COT (UTC−5) = <b>first block {bl(B['first']['number'])}</b> (ts {B['first']['ts']}, 00:00:00 COT; block {B['prev']['number']:,} is 23:59:59 on 10-27) to <b>last block {bl(B['last']['number'])}</b> (ts {B['last']['ts']}, 23:59:59 COT; block {B['next']['number']:,} is 00:00:00 on 10-29).<br>
<b>{len(txs)} transfers</b>, total <b>{fmt(total)} FLAPJAX</b> (2,480,000 exactly), to <b>{len(recips)} distinct recipients</b>.<br>
First-funder grouping gives <b>{ngroups} groups → {ngroups} distinct operators implied</b>: 1 group of {len(tre_grp)} recipients whose first BNB came from the treasury itself (G1), {funded_n-len(tre_grp)} recipients each with their own unique funder, and {never} recipients that have never received any BNB (no funder link possible, each counted alone). No two recipients share a non-treasury first funder. {ngroups+len(tre_grp)-1} if the treasury's gas drops are not treated as a link.<br>
<b>Unexplained transfers: none.</b> All {len(txs)} on-chain transfers have a matching row in the FlapJax paid-contributor list with the same hash and the same amount to the token, and every 2026-10-28 row in that list is on chain. Two FLAPJAX inflows also hit the treasury that day (a buyback and a 300,000 FJ return from a recipient), see notes A and B.
</div>

<h2>1. How the list was built and why it is complete</h2>
<ul>
<li>Block range: binary search on block timestamps (public BSC archive RPC) for the first block with ts ≥ 1793163600 (2026-10-28 05:00:00 UTC) and the first with ts ≥ 1793249600; block hashes: first <code>{B['first']['hash']}</code>, last <code>{B['last']['hash']}</code>.</li>
<li>Transfers: <code>eth_getLogs</code> on the token for <code>Transfer(from = treasury)</code> over blocks {B['first']['number']:,}–{B['last']['number']:,} in 5,000-block chunks → {len(txs)} logs. Every tx was then fetched: all are direct <code>transfer()</code> calls (selector 0xa9059cbb) sent by the treasury to the token contract, status 1, one Transfer log each.</li>
<li><b>Completeness check 1, nonce:</b> treasury nonce {V['tre_nonce_before']} at block {B['prev']['number']:,} and {V['tre_nonce_after']} at block {B['last']['number']:,} = 19 txs sent that day = 17 FLAPJAX transfers + 2 BNB-only txs: nonce 521 {tx('0x2d82a88d45b1f0229f4942a9457cf71eb1b40ab86e2489a1630982e23148d123')} (0.001406563965170797 BNB into the LI.FI diamond {ad('0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae')}, 09:33:29 COT) and nonce 526 {tx('0xa6f25856a9ce66cdf3ba4dba21bb2c19423610076db7ed7dd827d2029d1416fa')} (0.005 BNB multicall into the PancakeSwap router {ad('0x1b81d678ffb9c0263b24a97847620c99d213eb14')}, 13:41:46 COT, the daily buyback). No other tx left the treasury.</li>
<li><b>Completeness check 2, balance identity (exact to the wei):</b> balanceOf(treasury) {fmt(V['tre_bal_before'])} at block {B['prev']['number']:,} − {fmt(total)} out + {fmt(inflow)} in = {fmt(V['tre_bal_after'])} at block {B['last']['number']:,}. ✔ Inflows: {', '.join(tx(x['tx'])+' '+fmt(x['amt'])+' FJ from '+ad(x['frm']) for x in V['inflows'])}.</li>
<li><b>Receipts:</b> 17 of 17 tx hashes appear in the paid-contributor list on <a href='https://flapjax.surge.sh/'>flapjax.surge.sh</a> (rows dated 2026-10-28, snapshot "Updated 2026-10-29 14:36 COT"), amounts equal; the ledger has no 10-28 row that is missing from the chain.</li>
<li>First BNB funder: for each recipient, the earliest BNB inflow (normal tx or internal call) from the NodeReal transfer index (<code>nr_getAssetTransfers</code>), then re-checked on an archive node: BNB balance and nonce are both 0 at the block just before the funding tx, so nothing earlier exists. Recipients with no inflow were checked as nonce 0 and balance 0 at the last block of the day and at latest.</li>
</ul>

<h2>2. All {len(txs)} transfers (time in COT, amounts with 18 decimals)</h2>
<table><tr><th>#</th><th>Time COT</th><th>Block</th><th>Tx hash</th><th>Recipient</th><th class=r>Amount FLAPJAX</th><th>Group</th><th>Receipt in FlapJax ledger</th></tr>
{''.join(rows)}
<tr><th colspan=5>Total, {len(txs)} transfers, {len(recips)} recipients</th><th class=r><code>{fmt(total)}</code></th><th colspan=2></th></tr></table>

<h2>3. First BNB funder of each recipient</h2>
<table><tr><th>Group</th><th>Recipient</th><th class=r>10-28 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>

<h2>4. Groups and operator count</h2>
<ul>
<li><b>G1 · treasury-funded ({len(tre_grp)}):</b> {', '.join(ad(r) for r in tre_grp)}. First BNB for each was a 0.0005 BNB send from the treasury: {', '.join(tx(F[r]['tx'])+' (blk '+format(F[r]['block'],',')+')' for r in tre_grp)}. Under the task's rule these group "with the treasury". On chain this is FlapJax's own gas drop, so it links them to FlapJax, not to each other; if gas drops are not treated as a common operator, they are {len(tre_grp)} operators.</li>
<li><b>Own unique funder ({funded_n-len(tre_grp)}):</b> {', '.join(ad(r)+' ← '+ad(F[r]['funder']) for r in recips if F[r]['funded'] and F[r]['funder']!=TRE)}. Neither funder funded any other 10-28 recipient, and neither funder's own first funder is a recipient or the treasury.</li>
<li><b>Never received BNB ({never}):</b> {', '.join(ad(r) for r in recips if not F[r]['funded'])}. They hold FLAPJAX but have nonce 0, so no funder can link them; the chain cannot confirm or deny a shared operator.</li>
<li><b>Operators implied: {ngroups}</b> (strict funder rule, G1 counted once); {ngroups+len(tre_grp)-1} if the treasury gas drop is not counted as a link. {len(recips)} recipients, {len(txs)} transfers.</li>
</ul>

<h2>5. Notes and flags</h2>
<div class=flag><b>A. Daily buyback inflow.</b> {tx('0xa6f25856a9ce66cdf3ba4dba21bb2c19423610076db7ed7dd827d2029d1416fa',False)} (nonce 526, 0.005 BNB) returned 11,645,860.504603865910684550 FJ to the treasury from the FLAPJAX/WBNB pool {ad('0xca93ec685b8623688e6b236f9068034b298c2150')}. It is an inflow, not a payout, so it is outside the outflow list but is required for the balance identity to close.</div>
<div class=flag><b>B. A paid recipient sent FLAPJAX back.</b> {ad('0xca58696564492ef9c3229c89072365b253bda246',False)} (G1, gas-dropped by the treasury on 10-27) made its first ever tx, nonce 0, {tx('0x6ebd86862873513c0f9e69421bee5fad25721e70ac999b90f040a0d5c10d7dc8',False)}, returning 300,000 FJ to the treasury (block 130,347,025, 15:41:37 COT), about 2.4 h after receiving 150,000 FJ in {tx('0x00b7423c3baf0986ceda24d5286733747a5aed181106a8cf166f6319eacd94bd')} (ledger: secondopinion X01). The wallet spends gas the treasury gave it and returns tokens to the treasury; it looks like a FlapJax-controlled wallet rather than an outside agent. Worth checking whether its payouts should count as team, not contributor, spend.</div>
<div class=flag><b>C. Repeat recipients and no-gas wallets.</b> 6 of 10 recipients never received BNB (nonce 0), so the funder rule cannot test them. The ledger labels 5 of their 10-28 rows "funder: none (repeat paid operator", and {ad('0x97cd18b8bbb629b17675d868259ea84c0a2a5a9c')} gets three rows (Q74, proof_sip, Q46) in one day; these links are claims in the ledger text, not on chain.</div>
<p><small>The treasury's own first BNB: 0.12638071 BNB from {ad('0x5ba434b40be6cfc7033bf8d4545320fa609a4268')} (exchange-like, nonce over 100,000) in {tx('0xe0f7aeeedd38558ba63a596b8eb6b73e53af63d6282fc3e370160f3b2109bfd0')} (block 123,473,817). No 10-28 recipient shares it.</small></p>

<h2>6. Reproduce</h2>
<p>Raw data and scripts: <a href='q78/logs.json'>logs.json</a> ({len(txs)} raw Transfer logs), <a href='q78/verify.json'>verify.json</a> (boundaries, nonce, balances, tx checks), <a href='q78/funders.json'>funders.json</a> (first-funder txs and pre-block checks), <a href='q78/ledger.json'>ledger.json</a> (FlapJax receipt list parsed), scripts <a href='q78/day.py'>day.py</a>, <a href='q78/out.py'>out.py</a>, <a href='q78/verify.py'>verify.py</a>, <a href='q78/funders.py'>funders.py</a>, <a href='q78/funders2.py'>funders2.py</a>, <a href='q78/page.py'>page.py</a>. Any public BSC archive node reproduces the numbers; BscScan links on every hash.</p>
<p><small>Mirai · <a href='https://168-113-169-196.sslip.io/'>168-113-169-196.sslip.io</a> · no purchase, no buy, read-only.</small></p>
"""
open('flapjax-q78.html','w').write(html)
print(len(html), ngroups, len(recips), funded_n, never)
