#!/usr/bin/env python3
"""$PORCH identity cold-walk on eip155:4663 — two candidates, one question.
A: 0x4b43...ba3 (A.D's posted CA, claimed 'official' per @thePorchonRH X post)
B: 0xB628...0812 (Musepad-deployed, deploy tx 0x60b5d439...)
Deterministic, public bytes only. Emits a desk row."""
import json, sys, time, urllib.request
sys.path.insert(0, "/workspace/tools")
from eth_call import keccak256, selector, enc_addr

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-porchwalk/1.0"}
A = "0x4b434541873f171ab70d7d2f3a48b0f0b0f13ba3"   # a.d's CA
B = "0xB62810FA429ea5886B8d5ce4993494eaD6240812"   # musepad deploy
TX_B = "0x60b5d439e28a4987ddd64807613520a0e1c99ffaea5e5b603702d20e4efe9b90"
MB = "0x91a2dae9699f0b82540b5886b0d8759c22820ba3"  # canonical musebook
CHUNK = 4000

_i = 0
CALLS = 0
def rpc(method, params, budget=25):
    global _i, CALLS
    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:
            CALLS += 1
            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)

def call(to, sig, arg=""):
    data = "0x" + selector(sig) + arg
    r = rpc("eth_call", [{"to": to, "data": data}, "latest"])
    return r

def dec_str(hexr):
    # abi string: offset(32) len(32) data
    if not hexr or hexr == "0x": return None
    b = bytes.fromhex(hexr[2:])
    if len(b) < 64: return None
    ln = int.from_bytes(b[32:64], "big")
    return b[64:64+ln].decode("utf-8", "replace")

def dec_uint(hexr):
    if not hexr or hexr == "0x": return None
    return int(hexr, 16)

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

t0 = time.time()
head = int(rpc("eth_blockNumber", []), 16)
print(f"head {head}")

out = {"head": head, "candidates": {}}
for label, addr in [("A_ad", A), ("B_musepad", B)]:
    row = {"address": addr}
    code = rpc("eth_getCode", [addr, "latest"])
    row["code_bytes"] = (len(code) - 2) // 2 if code and code != "0x" else 0
    row["name"] = dec_str(call(addr, "name()"))
    row["symbol"] = dec_str(call(addr, "symbol()"))
    row["decimals"] = dec_uint(call(addr, "decimals()"))
    ts = dec_uint(call(addr, "totalSupply()"))
    row["totalSupply_raw"] = ts
    row["totalSupply"] = ts / 10**row["decimals"] if ts and row["decimals"] is not None else ts
    # clone detection — 1167-family variants: impl = 40 hex between a '73'
    # marker byte and the '5af4' tail, works for both prefix shapes
    if code and row["code_bytes"] in (44, 45):
        import re
        m = re.search(r"73([0-9a-fA-F]{40})5af4", code[2:])
        if m:
            row["clone_impl"] = "0x" + m.group(1)
    # first transfer -> deploy bound; scan window ~45min back
    first_tx = None
    win_start = max(0, head - 9000)
    a = win_start
    found = None
    while a <= head and found is None:
        b = min(a + CHUNK - 1, head)
        logs = rpc("eth_getLogs", [{"address": addr, "fromBlock": hex(a), "toBlock": hex(b),
                   "topics": [TRANSFER]}])
        if logs:
            found = logs[0]
        a = b + 1
    if found:
        row["first_transfer"] = {"block": int(found["blockNumber"], 16),
                                 "tx": found["transactionHash"],
                                 "from": "0x" + found["topics"][1][-40:],
                                 "to": "0x" + found["topics"][2][-40:]}
        rc = rpc("eth_getTransactionReceipt", [found["transactionHash"]])
        if rc:
            row["first_transfer"]["contract_created"] = rc.get("contractAddress")
            row["first_transfer"]["tx_from"] = rc.get("from")
            row["first_transfer"]["tx_to"] = rc.get("to")
        blk = rpc("eth_getBlockByNumber", [found["blockNumber"], False])
        if blk:
            row["first_transfer"]["block_ts"] = int(blk["timestamp"], 16)
    # count transfers (activity proxy) over same window
    n_tr = 0
    a = win_start
    while a <= head:
        b = min(a + CHUNK - 1, head)
        logs = rpc("eth_getLogs", [{"address": addr, "fromBlock": hex(a), "toBlock": hex(b),
                   "topics": [TRANSFER]}])
        n_tr += len(logs)
        a = b + 1
    row["transfer_events_window"] = n_tr
    out["candidates"][label] = row

# B's deploy tx receipt (named by musepad)
rc = rpc("eth_getTransactionReceipt", [TX_B])
if rc:
    out["B_deploy_tx"] = {"block": int(rc["blockNumber"], 16),
                          "from": rc.get("from"), "to": rc.get("to"),
                          "contract_created": rc.get("contractAddress"),
                          "status": rc.get("status")}
    blk = rpc("eth_getBlockByNumber", [rc["blockNumber"], False])
    if blk: out["B_deploy_tx"]["block_ts"] = int(blk["timestamp"], 16)

print(json.dumps(out, indent=1))
print(f"cost-line: {CALLS} rpc calls, {time.time()-t0:.1f}s wall, $0 (public RPC)")
