#!/usr/bin/env python3
# Enumerate every outgoing tx from an address by binary-searching
# eth_getTransactionCount at historical heights (archive node needed).
# For each nonce transition block, fetch the block's txs and classify
# the owner's sends (deploy/erc20/approvals/native/other).
import json, sys, time, urllib.request

RPCS = ["https://rpc.ordofi.network", "https://rpc.mainnet.chain.robinhood.com"]
OWNER = "0x18fb17662ba67a0714ff8551d30072bc04cd9d6b".lower()
TOKEN = "0x7804Fff73Eb6569DCc673fa345cf89BB905e2a8f".lower()
PAIR = "0x2f8473AE28Cd459457Ea79c0dB11d38A3BF2a605".lower()
DEPLOY_BLOCK = 69493407
_id = [0]

def rpc(method, params, timeout=25):
    _id[0] += 1
    body = json.dumps({"jsonrpc": "2.0", "id": _id[0], "method": method,
                       "params": params}).encode()
    last = None
    for url in RPCS:
        try:
            req = urllib.request.Request(url, data=body,
                                         headers={"Content-Type": "application/json"})
            with urllib.request.urlopen(req, timeout=timeout) as r:
                d = json.loads(r.read())
            if "error" in d:
                last = f"{url}: {d['error']}"
                continue
            return d["result"]
        except Exception as e:
            last = f"{url}: {e}"
    raise RuntimeError(f"{method} failed on all doors: {last}")

def nonce_at(addr, block):
    return int(rpc("eth_getTransactionCount", [addr, hex(block)]), 16)

def block_txs(block):
    b = rpc("eth_getBlockByNumber", [hex(block), True])
    return b["transactions"] if b else []

def receipt(txh):
    return rpc("eth_getTransactionReceipt", [txh])

SIGS = {"a9059cbb": "transfer", "095ea7b3": "approve", "23b872dd": "transferFrom",
        "e8e33700": "addLiquidity", "f305d719": "addLiquidityETH",
        "40c10f19": "mint", "a0712d68": "mint/seed", "06fdde03": "name"}

def classify(tx):
    to = (tx.get("to") or "").lower()
    inp = tx.get("input") or "0x"
    sel = inp[2:10]
    value = int(tx.get("value", "0x0"), 16)
    kind = []
    if to == "": kind.append("CONTRACT_CREATION")
    if to == TOKEN: kind.append("->MG-token")
    if to == PAIR: kind.append("->MG-pair")
    if sel in SIGS: kind.append(SIGS[sel])
    elif sel: kind.append(f"sel:{sel}")
    if value: kind.append(f"value:{value/1e18:g}")
    return ",".join(kind) or "plain-send"

def main():
    head = int(rpc("eth_blockNumber", []), 16)
    n_head = nonce_at(OWNER, head)
    n_deploy = nonce_at(OWNER, DEPLOY_BLOCK)
    print(f"head={head} nonce(head)={n_head} nonce(deploy_blk)={n_deploy}")

    # binary search first block with count >= n, for each transition
    trans_blocks = {}   # block -> set of nonce marks
    for n in range(n_deploy + 1, n_head + 1):
        lo, hi = DEPLOY_BLOCK + 1, head
        while lo < hi:
            mid = (lo + hi) // 2
            if nonce_at(OWNER, mid) >= n:
                hi = mid
            else:
                lo = mid + 1
            time.sleep(0.05)
        trans_blocks.setdefault(lo, set()).add(n)
        print(f"nonce {n}: first seen at block {lo}")
        time.sleep(0.05)

    rows = []
    for blk in sorted(trans_blocks):
        for tx in block_txs(blk):
            if (tx.get("from") or "").lower() != OWNER:
                continue
            r = receipt(tx["hash"])
            status = r.get("status") if r else None
            nlogs = len(r.get("logs", [])) if r else 0
            rows.append({
                "block": blk, "nonce": int(tx["nonce"], 16),
                "hash": tx["hash"], "to": (tx.get("to") or "CREATE"),
                "kind": classify(tx), "status": status, "logs": nlogs,
                "gasUsed": int(r["gasUsed"], 16) if r else None,
            })
            time.sleep(0.05)
    print(json.dumps(rows, indent=1))
    with open("/workspace/deliverables/musebook-collision/owner_sends_4663.json", "w") as f:
        json.dump({"owner": OWNER, "head": head, "nonce_head": n_head,
                   "rows": rows}, f, indent=1)

if __name__ == "__main__":
    main()
