#!/usr/bin/env python3
"""ERC-1967 and ERC-7201 slots computed with the Python standard library only.

hashlib.sha3_256 is the NIST SHA3-256, whose padding differs from Ethereum's
Keccak-256, so Keccak-f[1600] is written out here.
"""

RC = [
    0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000,
    0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
    0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
    0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003,
    0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
    0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
]
ROT = [[0, 36, 3, 41, 18], [1, 44, 10, 45, 2], [62, 6, 43, 15, 61],
       [28, 55, 25, 21, 56], [27, 20, 39, 8, 14]]
MASK = (1 << 64) - 1


def _rol(v, n):
    return ((v << n) | (v >> (64 - n))) & MASK if n else v


def _keccak_f(a):
    for rc in RC:
        c = [a[x][0] ^ a[x][1] ^ a[x][2] ^ a[x][3] ^ a[x][4] for x in range(5)]
        d = [c[(x - 1) % 5] ^ _rol(c[(x + 1) % 5], 1) for x in range(5)]
        a = [[a[x][y] ^ d[x] for y in range(5)] for x in range(5)]
        b = [[0] * 5 for _ in range(5)]
        for x in range(5):
            for y in range(5):
                b[y][(2 * x + 3 * y) % 5] = _rol(a[x][y], ROT[x][y])
        a = [[b[x][y] ^ (~b[(x + 1) % 5][y] & b[(x + 2) % 5][y]) for y in range(5)] for x in range(5)]
        a[0][0] ^= rc
    return a


def keccak256(data: bytes) -> bytes:
    rate = 136
    msg = bytearray(data) + b'\x01'          # Keccak padding (SHA3 uses 0x06)
    msg += b'\x00' * (-len(msg) % rate)
    msg[-1] |= 0x80
    a = [[0] * 5 for _ in range(5)]
    for off in range(0, len(msg), rate):
        for i in range(rate // 8):
            a[i % 5][i // 5] ^= int.from_bytes(msg[off + 8 * i:off + 8 * i + 8], 'little')
        a = _keccak_f(a)
    return b''.join(a[i % 5][i // 5].to_bytes(8, 'little') for i in range(4))


def h(n: int) -> str:
    return '0x' + n.to_bytes(32, 'big').hex()


def erc1967(label: str) -> str:
    """bytes32(uint256(keccak256(label)) - 1)"""
    return h(int.from_bytes(keccak256(label.encode()), 'big') - 1)


def erc7201(namespace: str) -> str:
    """keccak256(abi.encode(uint256(keccak256(id)) - 1)) & ~bytes32(uint256(0xff))"""
    inner = int.from_bytes(keccak256(namespace.encode()), 'big') - 1
    outer = int.from_bytes(keccak256(inner.to_bytes(32, 'big')), 'big')
    return h(outer & ~0xff)


if __name__ == '__main__':
    assert keccak256(b'').hex() == 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'
    for label in ('implementation', 'admin', 'beacon'):
        print(f'eip1967.proxy.{label:<15}', erc1967(f'eip1967.proxy.{label}'))
    for ns in ('example.main', 'openzeppelin.storage.Ownable',
               'openzeppelin.storage.Initializable', 'debimate.vault'):
        print(f'erc7201:{ns:<35}', erc7201(ns))
