#!/usr/bin/env python3 """ Resolve ENS Names for Public Haus Members This script resolves ENS names for Public Haus members imported from the DAOhaus API on Optimism mainnet. It updates the contacts with ENS names and profile information, and links them to the data source. """ import os import sys import logging from typing import Dict, Any, List, Optional from web3 import Web3 from dotenv import load_dotenv # Add parent directory to path to import utils sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from utils.db_connector import DatabaseConnector from utils.ens_resolver import ENSResolver from utils.logger import setup_logger # Load environment variables load_dotenv() # Setup logging logger = setup_logger("public_haus_ens_resolver") class PublicHausENSResolver: """Resolver for ENS names of Public Haus members""" def __init__(self): """Initialize the resolver""" # Initialize database self.db = DatabaseConnector() # Initialize Web3 and ENS resolver alchemy_api_key = os.getenv("ALCHEMY_API_KEY") if not alchemy_api_key: raise ValueError("ALCHEMY_API_KEY not found in environment variables") self.web3 = Web3(Web3.HTTPProvider(f"https://eth-mainnet.g.alchemy.com/v2/{alchemy_api_key}")) self.ens_resolver = ENSResolver(self.web3) # Get data source ID self.data_source_id = self.get_data_source_id() def get_data_source_id(self) -> str: """Get the ID of the Public Haus DAO API data source""" query = 'SELECT id FROM "DataSource" WHERE name = %(name)s' result = self.db.execute_query(query, {"name": "Public Haus DAO Tokens"}) if not result: raise ValueError("Public Haus DAO Tokens data source not found") return result[0]["id"] def get_public_haus_members(self) -> List[Dict[str, Any]]: """Get all Public Haus members from the database""" query = """ SELECT c.id, c."ethereumAddress", c."ensName" FROM "Contact" c JOIN "DaoMembership" dm ON c.id = dm."contactId" WHERE dm."daoName" = 'Public Haus' """ return self.db.execute_query(query) def resolve_ens_for_member(self, contact_id: str, ethereum_address: str, current_ens: Optional[str] = None) -> bool: """ Resolve ENS name for a member and update their profile. Args: contact_id: ID of the contact ethereum_address: Ethereum address of the member current_ens: Current ENS name of the member, if any Returns: True if ENS was resolved or already exists, False otherwise """ # Skip if already has ENS if current_ens: logger.info(f"Contact {contact_id} already has ENS: {current_ens}") # Still update profile from ENS if needed self.ens_resolver.update_contact_from_ens(contact_id, current_ens) # Link to data source self.db.link_contact_to_data_source(contact_id, self.data_source_id) return True # Resolve ENS name ens_name = self.ens_resolver.get_ens_name(ethereum_address) if not ens_name: logger.info(f"No ENS name found for {ethereum_address}") # Still link to data source self.db.link_contact_to_data_source(contact_id, self.data_source_id) return False # Update contact with ENS name self.db.update_contact(contact_id, {"ensName": ens_name}) logger.info(f"Updated contact {contact_id} with ENS name: {ens_name}") # Update profile from ENS self.ens_resolver.update_contact_from_ens(contact_id, ens_name) # Link to data source self.db.link_contact_to_data_source(contact_id, self.data_source_id) return True def run(self): """Run the resolver""" logger.info("Starting ENS resolution for Public Haus members") # Get all Public Haus members members = self.get_public_haus_members() logger.info(f"Found {len(members)} Public Haus members") # Resolve ENS for each member resolved_count = 0 for member in members: if self.resolve_ens_for_member( member["id"], member["ethereumAddress"], member.get("ensName") ): resolved_count += 1 logger.info(f"Resolved ENS for {resolved_count} out of {len(members)} members") return resolved_count def main(): """Main function""" try: resolver = PublicHausENSResolver() resolved_count = resolver.run() logger.info(f"ENS resolution completed successfully. Resolved {resolved_count} members.") return 0 except Exception as e: logger.exception(f"Error resolving ENS names: {e}") return 1 if __name__ == "__main__": sys.exit(main())