"""One-shot: grant the operator wallet ``authorized`` on ReputationRegistry. The deployed ``ReputationRegistry`` contract gates ``updateOnAuction``, ``updateOnQuality``, ``updateOnFee``, and ``slashReputation`` behind an ``onlyAuthorized`` modifier. The contract owner is implicitly authorized (``msg.sender == owner`` is accepted), and the constructor seeds ``authorized[owner] = true``, so when the operator wallet IS the owner this script is a no-op and exits 0 without sending a tx. Usage:: python scripts/grant_reputation_authority.py # operator self python scripts/grant_reputation_authority.py 0xDeadBeef # explicit grantee The signer is always the operator wallet (``HACKATHON_WALLET_PRIVATE_KEY``); the optional positional arg is the *grantee* address. Only the contract owner can call ``setAuthorized``. """ from __future__ import annotations import argparse import asyncio import logging import os import sys from pathlib import Path # Make ``polyglot_alpha`` importable when run as a plain script. sys.path.insert(0, str(Path(__file__).resolve().parents[1])) try: from dotenv import load_dotenv load_dotenv() except ImportError: # pragma: no cover - optional pass from web3 import Web3 from polyglot_alpha.chain.reputation_registry import ( is_authorized, set_authorized, ) from polyglot_alpha.onchain import OnChainClient logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s %(message)s", ) logger = logging.getLogger("grant_reputation_authority") def _parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "grantee", nargs="?", default=None, help=( "Address to grant 'authorized' to. Defaults to " "HACKATHON_WALLET_ADDRESS (the operator wallet)." ), ) parser.add_argument( "--revoke", action="store_true", help="Revoke instead of grant (calls setAuthorized(addr, false)).", ) return parser.parse_args() async def main() -> int: args = _parse_args() operator_addr = os.environ.get("HACKATHON_WALLET_ADDRESS") if not operator_addr: logger.error("HACKATHON_WALLET_ADDRESS not set") return 2 grantee = args.grantee or operator_addr grantee = Web3.to_checksum_address(grantee) operator_addr = Web3.to_checksum_address(operator_addr) client = OnChainClient() owner = Web3.to_checksum_address( client.reputation.functions.owner().call() ) logger.info( "ReputationRegistry @ %s — owner=%s, operator=%s, grantee=%s", client.config.reputation_registry, owner, operator_addr, grantee, ) if owner != operator_addr: logger.error( "operator wallet is NOT the contract owner; setAuthorized " "would revert. Owner=%s, operator=%s", owner, operator_addr, ) return 3 already = await is_authorized(grantee) if already and not args.revoke: logger.info( "grantee %s is already authorized (owner-implicit or explicit); " "skipping setAuthorized", grantee, ) return 0 if (not already) and args.revoke: logger.info( "grantee %s is not currently authorized; nothing to revoke", grantee, ) return 0 desired = not args.revoke logger.info( "sending setAuthorized(%s, %s)…", grantee, desired ) tx_hash = await set_authorized(grantee, desired) logger.info("setAuthorized tx=%s", tx_hash) # Confirm new state. new_state = await is_authorized(grantee) logger.info( "post-tx authorized[%s] = %s (expected %s)", grantee, new_state, desired, ) if bool(new_state) != bool(desired): logger.error("authorization state did not flip — check tx receipt") return 4 return 0 if __name__ == "__main__": sys.exit(asyncio.run(main()))