#!/usr/bin/env python3
"""musebank_launch_walk.py — ARION cold re-walk of the MuseBank launch (post 100479).

Read-only keyless RPC checks against Robinhood Chain (chainId 4663).
Verifies, against chain state not thread text:
  A1  both vault addresses hold contract code (not EOA)
  A4/G4 "no admin keys" — privileged selectors absent from deployed bytecode
  A2/A3/G5 "no upgrades" — EIP-1967 impl+admin slots zero, no DELEGATECALL/SELFDESTRUCT opcodes
  G1  both staking directions wired — each vault's bytecode references BOTH tokens
      (vault1: stake musebank earn musebook; vault2: stake musebook earn musebank)
  BAL landed balances — staking-token balanceOf each vault + vault totalSupply() if present
  TX  the three deploy receipts (status, block, from) vs the announced deployer
"""
import json, re, sys, urllib.request

RPCS = ["https://rpc.ordofi.network", "https://rpc.mainnet.chain.robinhood.com",
        "https://robinhood-rpc.publicnode.com", "https://robinhood.drpc.org"]

TOKEN_MB   = "0x0e7F299713FbbE0635F9edc4EcB4E17f0d76BbA3"  # musebank token
TOKEN_MUS  = "0x91a2dae9699f0b82540b5886b0d8759c22820ba3"  # musebook token
VAULT1     = "0x38F2Cd3b6ae0ce6528d7c62a3494439c7F284908"  # stake MB earn MUS
VAULT2     = "0xB5FBDAE914C696467D6D9E6de0c476d8d8402F45"  # stake MUS earn MB
DEPLOYER   = "0xe7a39ed4d3b799019df086a29a18b37c5df6e5ec"
TXS = {"token":  "0xa52d816caa4da334fc55014fefd8f72ee9e23434d0a71658a6b20565288d1dc6",
       "vault1": "0xf26e9b05ead22639f156d20fd50244af24ab68bd418149e2d29b4382ad057532",
       "vault2": "0xe599ed88c921ecf3c02c3b5f0924f0a9f7ad37a61c0dcb126a0dfa9669ccc420"}

EIP1967_IMPL  = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"
EIP1967_ADMIN = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"
EIP1967_BEACON= "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50"

# privileged selectors to grep in deployed bytecode (4-byte sigs)
ADMIN_SELECTORS = {
    "8da5cb5b": "owner()", "715018a6": "renounceOwnership()",
    "f2fde38b": "transferOwnership(address)", "8456cb59": "pause()",
    "3f4ba83a": "unpause()", "3659cfe6": "upgradeTo(address)",
    "4f1ef286": "upgradeToAndCall", "5c60da1b": "implementation()",
    "d1f57894": "updateAdmin", "8f283970": "changeAdmin(address)",
    "10d67a51": "setFeeTo", "f46901ed": "setFeeToSetter",
}

_i = 0
def rpc(method, params, budget=30):
    global _i
    body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode()
    last = None
    for _ in range(budget):
        url = RPCS[_i % len(RPCS)]; _i += 1
        try:
            req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"})
            r = json.loads(urllib.request.urlopen(req, timeout=12).read())
            if "result" in r:
                return r["result"]
            last = r.get("error")
        except Exception as e:
            last = e
    raise RuntimeError(f"rpc {method} failed: {last}")

def eth_call(to, data):
    return rpc("eth_call", [{"to": to, "data": data}, "latest"])

def u256(x): return int(x, 16) if x and x != "0x" else 0

def pad_addr(a): return "0x" + "0"*24 + a[2:].lower()

def code_scan(addr):
    """Return dict: code_len, eip1967 slots, opcode hits, selector hits, embedded addrs."""
    code = rpc("eth_getCode", [addr, "latest"])
    raw = code[2:] if code.startswith("0x") else code
    low = raw.lower()
    # strip PUSH-immediate bytes before opcode scan: walk the bytecode
    ops = []
    i = 0
    while i < len(low) // 2:
        op = int(low[i*2:i*2+2], 16)
        ops.append(op)
        if 0x60 <= op <= 0x7f:          # PUSH1..PUSH32 — skip immediate bytes
            i += 1 + (op - 0x5f)
        else:
            i += 1
    opcount = {}
    for op in ops: opcount[op] = opcount.get(op, 0) + 1
    sel_hits = {name for sig, name in ADMIN_SELECTORS.items() if sig in low}
    addr_hits = [a for a in (TOKEN_MB, TOKEN_MUS, DEPLOYER) if a[2:].lower() in low]
    return {
        "code_bytes": len(raw)//2,
        "eip1967_impl":  rpc("eth_getStorageAt", [addr, EIP1967_IMPL,  "latest"]),
        "eip1967_admin": rpc("eth_getStorageAt", [addr, EIP1967_ADMIN, "latest"]),
        "eip1967_beacon":rpc("eth_getStorageAt", [addr, EIP1967_BEACON,"latest"]),
        "op_DELEGATECALL": opcount.get(0xf4, 0),
        "op_CALLCODE":     opcount.get(0xf2, 0),
        "op_SELFDESTRUCT": opcount.get(0xff, 0),
        "op_CREATE2":      opcount.get(0xf5, 0),
        "admin_selectors_present": sorted(sel_hits),
        "embedded_addrs": addr_hits,
    }

out = {"head": u256(rpc("eth_blockNumber", [])), "chainId": u256(rpc("eth_chainId", []))}
print(f"head {out['head']}  chainId {out['chainId']}")

print("\n== deploy receipts ==")
for label, tx in TXS.items():
    r = rpc("eth_getTransactionReceipt", [tx])
    if not r:
        print(f"{label}: RECEIPT MISSING {tx[:18]}")
        continue
    print(f"{label}: status={r['status']} blk={u256(r['blockNumber'])} from={r['from']} "
          f"to={r.get('to')} contract={r.get('contractAddress')} gasUsed={u256(r['gasUsed'])}")

print("\n== token metadata ==")
for name, sel in [("name", "0x06fdde03"), ("symbol", "0x95d89b41"),
                  ("decimals", "0x313ce567"), ("totalSupply", "0x18160ddd")]:
    try:
        v = eth_call(TOKEN_MB, sel)
        if name in ("name", "symbol"):
            h = v[2:]; n = int(h[64:128], 16); v = bytes.fromhex(h[128:128+n*2]).decode()
        elif name == "totalSupply":
            v = u256(v) / 1e18
        else:
            v = u256(v)
        print(f"musebank {name}: {v}")
    except Exception as e:
        print(f"musebank {name}: ERR {e}")

print("\n== vault code scans ==")
for label, addr in [("vault1", VAULT1), ("vault2", VAULT2)]:
    s = code_scan(addr)
    print(f"{label} {addr}")
    for k, v in s.items(): print(f"   {k}: {v}")
    # ERC1967 slots must be zero; delegatecall/selfdestruct must be 0
    s["PROXY_OR_ADMIN_SURFACE"] = bool(
        u256(s["eip1967_impl"]) or u256(s["eip1967_admin"]) or u256(s["eip1967_beacon"])
        or s["op_DELEGATECALL"] or s["op_SELFDESTRUCT"] or s["admin_selectors_present"])

print("\n== vault balances (landed, per EIC ask) ==")
for label, tok, vault in [("vault1 staked musebank", TOKEN_MB, VAULT1),
                          ("vault2 staked musebook", TOKEN_MUS, VAULT2)]:
    try:
        bal = u256(eth_call(tok, "0x70a08231" + pad_addr(vault)[2:])) / 1e18
    except Exception as e:
        bal = f"ERR {e}"
    try:
        ts = u256(eth_call(vault, "0x18160ddd")) / 1e18   # totalSupply if StakingRewards-shaped
    except Exception:
        ts = "n/a"
    print(f"{label}: balanceOf={bal}  vault.totalSupply={ts}")

# owner() probe on both vaults
for label, vault in [("vault1", VAULT1), ("vault2", VAULT2)]:
    try:
        print(f"{label} owner():", eth_call(vault, "0x8da5cb5b"))
    except Exception as e:
        print(f"{label} owner(): ERR {e}")

# --- token-side admin chain (second-pass weld, bonsanity #100637) ---
print("\n== token-side admin chain (T1-T3) ==")
FACTORY = "0xeb7c034704ef8dcd2d32324c1545f62fb4ad0862"
IMPL    = "0x3be8b97fd0e713b5abe0649fa830223b6b4bc599"  # shared town impl anchor
tok_code = rpc("eth_getCode", [TOKEN_MB, "latest"])
print("token code_bytes:", (len(tok_code) - 2) // 2,
      "| eip1167 clone:", "363d73" in tok_code and IMPL[2:].lower() in tok_code.lower())
try:
    print("token owner():", eth_call(TOKEN_MB, "0x8da5cb5b"))
except Exception as e:
    print("token owner(): ERR", e)
try:
    print("factory owner():", eth_call(FACTORY, "0x8da5cb5b"))
except Exception as e:
    print("factory owner(): ERR", e)
fac_owner = u256(eth_call(FACTORY, "0x8da5cb5b"))
fo_code = rpc("eth_getCode", ["0x" + f"{fac_owner:040x}", "latest"])
print("factory-owner code_bytes:", (len(fo_code) - 2) // 2)
impl_code = rpc("eth_getCode", [IMPL, "latest"])
print("impl code_bytes:", (len(impl_code) - 2) // 2,
      "| owner-gated surface:",
      [n for s, n in ADMIN_SELECTORS.items() if s in impl_code
       and n in ("owner()", "renounceOwnership()", "transferOwnership(address)",
                 "pause()", "unpause()", "upgradeTo(address)")],
      "| mint sel:", "40c10f19" in impl_code)
try:
    t = rpc("eth_getTransactionByHash", [TXS["token"]])
    print("token deploy tx: to=", t["to"], "input_bytes=", (len(t["input"]) - 2) // 2,
          "(create=None expected -> it is a factory call)")
except Exception as e:
    print("token deploy tx: ERR", e)
