#!/usr/bin/env python3
"""fm_entrysim: entry-time sell sim for the s777 lane (exec, 28 Oct 2026, #4251). Port of research /root/tm/s777/entrysim.py + /opt/sw/sellsim
(UniversalRouter eth_call with stateOverride, sells 1% / 0.1% of supply and 0.1 ETH worth) with a pure-python keccak and trading_rpc. No money moves.
blocked(pool_key_block, pool_id, token) -> (bool, info): BLOCK = 1% or 0.1% sell reverts; ratio<0.5 = tax/hook. Inconclusive/exception = blocked (fail closed)."""
import sys
sys.path.insert(0, "/opt/fm")
import trading_rpc as T
_RC = [0x1,0x8082,0x800000000000808a,0x8000000080008000,0x808b,0x80000001,0x8000000080008081,0x8000000000008009,0x8a,0x88,0x80008009,0x8000000a,0x8000808b,0x800000000000008b,0x8000000000008089,0x8000000000008003,0x8000000000008002,0x8000000000000080,0x800a,0x800000008000000a,0x8000000080008081,0x8000000000008080,0x80000001,0x8000000080008008]
_RO = [1,3,6,10,15,21,28,36,45,55,2,14,27,41,56,8,25,43,62,18,39,61,20,44]
_PI = [10,7,11,17,18,3,5,16,8,21,24,4,15,23,19,13,12,2,20,14,22,9,6,1]
M = (1 << 64) - 1
def _f(s):
    for rc in _RC:
        c = [s[x] ^ s[x+5] ^ s[x+10] ^ s[x+15] ^ s[x+20] for x in range(5)]
        for x in range(5):
            d = c[(x+4) % 5] ^ (((c[(x+1) % 5] << 1) | (c[(x+1) % 5] >> 63)) & M)
            for y in range(0, 25, 5): s[y+x] ^= d
        t = s[1]
        for i in range(24):
            j = _PI[i]; b = s[j]; r = _RO[i]
            s[j] = ((t << r) | (t >> (64 - r))) & M; t = b
        for y in range(0, 25, 5):
            a = s[y:y+5]
            for x in range(5): s[y+x] = a[x] ^ ((~a[(x+1) % 5]) & a[(x+2) % 5])
        s[0] ^= rc
def k(data):
    s = [0] * 25; rate = 136
    p = bytearray(data); p.append(1)
    while len(p) % rate: p.append(0)
    p[-1] |= 0x80
    for o in range(0, len(p), rate):
        for i in range(rate // 8): s[i] ^= int.from_bytes(p[o+8*i:o+8*i+8], "little")
        _f(s)
    return b"".join(s[i].to_bytes(8, "little") for i in range(4))
assert k(b"").hex() == "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
def rpc(m, p): return T.call(m, p)
W = "0x1111111111111111111111111111111111111111"
P2 = "0x000000000022d473030f116ddee9f6b43ac78ba3"
UR = "0x8876789976decbfcbbbe364623c63652db8c0904"
PM = "0x8366a39cc670b4001a1121b8f6a443a643e40951"
TI = "0xdd466e674ea557f56295e2d0218a125ea4b4f0f6f3307b95f85e6110838d6438"
BIG = 1 << 200
def pad(x, n=32):
    if isinstance(x, int): return x.to_bytes(n, "big")
    if isinstance(x, str): x = bytes.fromhex(x[2:])
    return x.rjust(n, b"\0")
def h(b): return "0x" + b.hex()
def call(to, data, ov=None):
    return rpc("eth_call", [{"from": W, "to": to, "data": data}, "latest"] + ([ov] if ov else []))
def probe_balance(tok):
    cs = [h(k(pad(W) + pad(s))) for s in range(12)] + [h(k(pad(W)[12:] + b"\0"*8 + bytes.fromhex("87a211a2")))]
    for slot in cs:
        r = call(tok, "0x70a08231" + W[2:].rjust(64, "0"), {tok: {"stateDiff": {slot: h(pad(BIG))}}})
        if r.get("result") and int(r["result"], 16) == BIG: return slot
def probe_allow(tok):
    cs = [h(k(pad(P2) + k(pad(W) + pad(s)))) for s in range(12)] + [h(k(pad(W)[12:] + b"\0"*8 + bytes.fromhex("7f5e9f20") + pad(P2)))]
    for slot in cs:
        r = call(tok, "0xdd62ed3e" + W[2:].rjust(64, "0") + P2[2:].rjust(64, "0"), {tok: {"stateDiff": {slot: h(pad(BIG))}}})
        if r.get("result") and int(r["result"], 16) == BIG: return slot
def pool_key(pid, block):
    r = rpc("eth_getLogs", [{"address": PM, "topics": [TI, pid], "fromBlock": hex(block), "toBlock": hex(block)}])
    l = r["result"][0]; d = bytes.fromhex(l["data"][2:])
    ts = int.from_bytes(d[32:64], "big"); ts = ts - (1 << 256) if ts >> 255 else ts
    return dict(c0="0x" + l["topics"][2][-40:], c1="0x" + l["topics"][3][-40:], fee=int.from_bytes(d[0:32], "big"), ts=ts % (1 << 256), hooks="0x" + d[64:96].hex()[-40:])
def build(key, token, amt, minout):
    w = pad; zfo = token.lower() == key["c0"].lower()
    cin, cout = (key["c0"], key["c1"]) if zfo else (key["c1"], key["c0"])
    sw = w(0x20) + w(key["c0"]) + w(key["c1"]) + w(key["fee"]) + w(key["ts"]) + w(key["hooks"]) + w(int(zfo)) + w(amt) + w(minout) + w(0x120) + w(0)
    p = [sw, w(cin) + w((1 << 128) - 1), w(cout) + w(0)]
    actions = bytes([6, 0x0c, 0x0f]); ab = w(len(actions)) + actions.ljust(32, b"\0")
    pb = w(len(p)); offs = b""; body = b""; o = 32 * len(p)
    for x in p:
        offs += w(o); seg = w(len(x)) + x.ljust((len(x) + 31) // 32 * 32, b"\0"); body += seg; o += len(seg)
    pb += offs + body
    inner = w(0x40) + w(0x40 + len(ab)) + ab + pb
    cb = w(1) + bytes([0x10]).ljust(32, b"\0")
    ib = w(1) + w(0x20) + w(len(inner)) + inner.ljust((len(inner) + 31) // 32 * 32, b"\0")
    return h(bytes.fromhex("3593564c") + w(0x60) + w(0x60 + len(cb)) + w(2 ** 40) + cb + ib)
def _out(r):
    d = r.get("error", {}).get("data", "")
    return int(d[-64:], 16) / 1e18 if isinstance(d, str) and len(d) >= 138 else None
def blocked(pid, tok, block):
    """-> (blocked, info dict). Fail closed on any inconclusive result."""
    o = {}
    try:
        key = pool_key(pid, block); tsup = int(rpc("eth_call", [{"to": tok, "data": "0x18160ddd"}, "latest"])["result"], 16)
        bb = probe_balance(tok); a = probe_allow(tok)
        if not bb or not a: return True, dict(note="slot_probe")
        slot2 = h(k(pad(UR) + k(pad(tok) + k(pad(W) + pad(1)))))
        def ov(amt): return {tok: {"stateDiff": {bb: h(pad(amt)), a: h(pad(BIG))}}, P2: {"stateDiff": {slot2: h(pad(((1 << 48) - 1) << 160 | (1 << 160) - 1))}}, W: {"balance": "0x" + "f" * 20}}
        for lab, frac in (("p1", 0.01), ("p01", 0.001)):
            amt = int(tsup * frac); o[lab + "_ok"] = int("result" in call(UR, build(key, tok, amt, 0), ov(amt)))
            o[lab + "_out"] = _out(call(UR, build(key, tok, amt, 1 << 120), ov(amt)))
        if not (o["p1_ok"] and o["p01_ok"]): return True, dict(o, note="BLOCK")
        if o.get("p01_out"):
            amt = min(int(0.1 / (o["p01_out"] / int(tsup * 0.001))), tsup)
            ro = _out(call(UR, build(key, tok, amt, 1 << 120), ov(amt)))
            if ro is not None:
                o["e01_ratio"] = round(ro / 0.1, 3)
                if o["e01_ratio"] < 0.5: return True, dict(o, note="ratio<0.5")
        return False, dict(o, note="PASS")
    except Exception as e:
        return True, dict(o, note="EXC " + repr(e)[:80])
if __name__ == "__main__":
    print(blocked(sys.argv[1], sys.argv[2], int(sys.argv[3])))
