"""lpclass.py (FW Analyst 1, A1.26 tag): LP class of a v3 pool at its first swap + 300 s block, same method as v3lpown.py.
EOA = largest in-range liquidity holder is a plain wallet; CONTRACT = it has code; BURNED = zero or dead address; ? = unknown.
Holder = ownerOf(tokenId) at that block for position-manager mints, else the Mint owner. Results cached in /root/fwa1/live/lpclass.json.
Usage: from lpclass import lp_class; lp_class(pool)   or   python3 lpclass.py <pool>"""
import json, os, sys, sqlite3, time, urllib.request
SPB = 0.101283
PM = "0x73991a25c818bf1f1128deaab1492d45638de0d3"
T_MINT = "0x7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde"
T_SWAP = "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67"
T_INC = "0x3067048beee31b25b2f1681f88dac838c8bba36af25bfb2b7cf7473a5847e35f"
DEAD = {"0x" + "0" * 40, "0x000000000000000000000000000000000000dead", "0xdead000000000000000000000000000000000000"}
CF = "/root/fwa1/live/lpclass.json"
K = json.load(open("/opt/common/drpc_paid.json"))["robinhood"]
src = open("/root/fwa1/v3pull.py").read().split("\n"); exec(next(l for l in src if l.startswith("U =")))
def post(body):
    for i in range(4):
        try:
            rq = urllib.request.Request(U, json.dumps(body).encode(), {"Content-Type": "application/json", "User-Agent": "Mozilla/5.0"})
            return json.load(urllib.request.urlopen(rq, timeout=60))
        except Exception as e:
            err = e; time.sleep(2 * (i + 1))
    raise err
def s256(h):
    v = int(h, 16); return v - (1 << 256) if v >= 1 << 255 else v
def _calc(pool):
    V = sqlite3.connect("file:/opt/sw/v3live.db?mode=ro", uri=True)
    r = V.execute("SELECT created_b FROM pools WHERE pool=?", (pool,)).fetchone()
    if not r: return "?"
    cb = r[0]; logs = []
    for f in range(cb, cb + 200000, 50000):
        x = post({"jsonrpc": "2.0", "id": 1, "method": "eth_getLogs", "params": [{"address": pool, "fromBlock": hex(f), "toBlock": hex(f + 49999), "topics": [[T_MINT, T_SWAP]]}]}).get("result") or []
        logs += x
        if any(l["topics"][0] == T_SWAP for l in logs): break
    sw = [l for l in logs if l["topics"][0] == T_SWAP]
    if not sw: return "?"
    key = lambda l: (int(l["blockNumber"], 16), int(l["logIndex"], 16))
    fb = min(int(l["blockNumber"], 16) for l in sw); b300 = fb + int(300 / SPB)
    if b300 > max(int(l["blockNumber"], 16) for l in logs):        # make sure the window reaches the signal block
        logs += post({"jsonrpc": "2.0", "id": 1, "method": "eth_getLogs", "params": [{"address": pool, "fromBlock": hex(max(int(l["blockNumber"], 16) for l in logs) + 1), "toBlock": hex(b300), "topics": [[T_MINT, T_SWAP]]}]}).get("result") or []
    last = max((l for l in logs if l["topics"][0] == T_SWAP and int(l["blockNumber"], 16) <= b300), key=key)
    tick = s256(last["data"][2 + 4 * 64:2 + 5 * 64])
    u = {}
    for l in sorted(logs, key=key):
        if l["topics"][0] != T_MINT or int(l["blockNumber"], 16) > b300: continue
        tl, tu = s256(l["topics"][2]), s256(l["topics"][3]); amt = int(l["data"][2 + 64:2 + 128], 16)
        if not (tl <= tick < tu): continue
        own = "0x" + l["topics"][1][-40:]
        if own == PM:
            rc = post({"jsonrpc": "2.0", "id": 1, "method": "eth_getTransactionReceipt", "params": [l["transactionHash"]]}).get("result") or {}
            tid = next((int(x["topics"][1], 16) for x in rc.get("logs", []) if x["address"].lower() == PM and x["topics"][0] == T_INC and int(x["data"][2:66], 16) == amt), None)
            o = post({"jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [{"to": PM, "data": "0x6352211e" + "%064x" % tid}, hex(b300)]}).get("result") if tid is not None else None
            own = "0x" + o[-40:] if o and len(o) >= 42 else "unknown"
        u[own] = u.get(own, 0) + amt
    if not u: return "?"
    top = max(u, key=u.get)
    if top in DEAD: return "BURNED"
    if top == "unknown": return "?"
    c = post({"jsonrpc": "2.0", "id": 1, "method": "eth_getCode", "params": [top, "latest"]}).get("result")
    return "EOA" if c in ("0x", "", None) else "CONTRACT"
def lp_class(pool):
    pool = pool.lower()
    C = json.load(open(CF)) if os.path.exists(CF) else {}
    if pool in C and C[pool] != "?": return C[pool]
    try: v = _calc(pool)
    except Exception: v = "?"
    C = json.load(open(CF)) if os.path.exists(CF) else {}
    C[pool] = v; json.dump(C, open(CF + ".tmp", "w")); os.replace(CF + ".tmp", CF)
    return v
if __name__ == "__main__":
    for p in sys.argv[1:]: print(p, lp_class(p))
