#!/usr/bin/env python3
"""MG-twin 0xCFfF30 distribution watch — chunked getLogs on eip155:4663.
Count Transfer/Approval events since deploy blk 73,499,878; also mint-pocket balance."""
import json, os, sys, time, urllib.request
sys.path.insert(0, "/workspace/tools")
from eth_call import keccak256

RPCS = ["https://rpc.ordofi.network", "https://rpc.mainnet.chain.robinhood.com",
        "https://robinhood-rpc.publicnode.com", "https://robinhood.drpc.org"]
H = {"Content-Type": "application/json", "User-Agent": "ARION-twinwatch/1.0"}
TWIN = "0xCFfF3090465d458a5fb718260DD401C399C8fa6B"
POCKET = "0x51a56d45"
DEPLOY = 73_499_878
CHUNK = 4000

_i = 0
def rpc(method, params, budget=25):
    global _i
    body = json.dumps({"jsonrpc":"2.0","id":1,"method":method,"params":params}).encode()
    t0, delay, last = time.time(), 0.4, None
    while True:
        url = RPCS[_i % len(RPCS)]; _i += 1
        try:
            d = json.load(urllib.request.urlopen(urllib.request.Request(url, data=body, headers=H), timeout=30))
            if "result" in d:
                time.sleep(0.05)
                return d["result"]
            last = d.get("error")
            if time.time() - t0 > budget: raise RuntimeError(f"budget: {last}")
            time.sleep(delay); delay = min(delay*1.7, 2.5)
        except Exception as e:
            last = e
            if time.time() - t0 > budget: raise RuntimeError(f"budget: {last}")
            time.sleep(delay); delay = min(delay*1.7, 2.5)

TRANSFER = "0x" + keccak256(b"Transfer(address,address,uint256)").hex()
APPROVAL = "0x" + keccak256(b"Approval(address,address,uint256)").hex()

CURSOR = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".twin_cursor")
start = DEPLOY
if os.path.exists(CURSOR):
    try:
        start = max(start, int(open(CURSOR).read().strip()))
    except Exception:
        pass

head = int(rpc("eth_blockNumber", []), 16)
print("head", head, "| twin age blocks:", head - DEPLOY, "| scan from", start, flush=True)

fails = 0
transfers, approvals = [], []
a = start
while a <= head:
    b = min(a + CHUNK - 1, head)
    try:
        logs = rpc("eth_getLogs", [{"address": TWIN, "fromBlock": hex(a), "toBlock": hex(b),
                   "topics": [[TRANSFER, APPROVAL]]}])
        for lg in logs:
            (transfers if lg["topics"][0] == TRANSFER else approvals).append(lg)
    except Exception as e:
        fails += 1
        print("FAIL chunk", a, b, str(e)[:100], flush=True)
    a = b + 1

print("chunk-fails", fails, "| transfers", len(transfers), "| approvals", len(approvals))
senders = {}
for lg in transfers:
    frm = "0x" + lg["topics"][1][-40:]
    to = "0x" + lg["topics"][2][-40:]
    amt = int(lg["data"], 16) / 1e18 if lg["data"] != "0x" else 0
    senders[frm] = senders.get(frm, 0) + 1
    print(f"  blk {int(lg['blockNumber'],16)} {frm[:10]}.. -> {to[:10]}.. amt {amt:,.0f} tx {lg['transactionHash'][:14]}")
for lg in approvals:
    own = "0x" + lg["topics"][1][-40:]
    sp = "0x" + lg["topics"][2][-40:]
    print(f"  APPROVAL blk {int(lg['blockNumber'],16)} owner {own[:10]}.. spender {sp[:10]}..")
print("senders:", {k[:12]: v for k, v in senders.items()})
if fails == 0:
    with open(CURSOR, "w") as f:
        f.write(str(head + 1))
    print("cursor ->", head + 1)
