Ejemplo n.º 1
0
    def _resolve(self, name: str, fn_name: str = 'addr') -> Optional[Union[ChecksumAddress, str]]:
        normal_name = normalize_name(name)

        resolver, current_name = self._get_resolver(normal_name, fn_name)
        if not resolver:
            return None

        node = self.namehash(normal_name)

        # handle extended resolver case
        if _resolver_supports_interface(resolver, EXTENDED_RESOLVER_INTERFACE_ID):
            contract_func_with_args = (fn_name, [node])

            calldata = resolver.encodeABI(*contract_func_with_args)
            contract_call_result = resolver.caller.resolve(
                ens_encode_name(normal_name), calldata
            )
            result = self._decode_ensip10_resolve_data(
                contract_call_result, resolver, fn_name
            )
            return to_checksum_address(result) if is_address(result) else result
        elif normal_name == current_name:
            lookup_function = getattr(resolver.functions, fn_name)
            result = lookup_function(node).call()
            if is_none_or_zero_address(result):
                return None
            return to_checksum_address(result) if is_address(result) else result
        return None
Ejemplo n.º 2
0
    def get_text(self, name: str, key: str) -> str:
        """
        Get the value of a text record by key from an ENS name.

        :param str name: ENS name to look up
        :param str key: ENS name's text record key
        :return: ENS name's text record value
        :rtype: str
        :raises UnsupportedFunction: If the resolver does not support the "0x59d1d43c" interface id
        :raises ResolverNotFound: If no resolver is found for the provided name
        """
        node = raw_name_to_hash(name)
        normal_name = normalize_name(name)

        r = self.resolver(normal_name)
        if r:
            if _resolver_supports_interface(r, GET_TEXT_INTERFACE_ID):
                return r.caller.text(node, key)
            else:
                raise UnsupportedFunction(
                    f"Resolver for name {name} does not support `text` function."
                )
        else:
            raise ResolverNotFound(
                f"No resolver found for name `{name}`. It is likely the name contains an "
                "unsupported top level domain (tld)."
            )
Ejemplo n.º 3
0
 def _setup_reverse(self, name, address, transact={}):
     if name:
         name = normalize_name(name)
     else:
         name = ''
     transact['from'] = address
     return self._reverse_registrar().setName(name, transact=transact)
Ejemplo n.º 4
0
 def _setup_reverse(self, name: str, address: ChecksumAddress, transact: 'TxDict'={}) -> Hash32:
     if name:
         name = normalize_name(name)
     else:
         name = ''
     transact['from'] = address
     return self._reverse_registrar().functions.setName(name).transact(transact)
Ejemplo n.º 5
0
    def _ens_lookup(self, web3: Optional[Web3],
                    name: str) -> Optional[ChecksumEthAddress]:
        """Performs an ENS lookup and returns address if found else None

        May raise:
        - RemoteError if Etherscan is used and there is a problem querying it or
        parsing its response
        """
        if web3 is not None:
            return web3.ens.resolve(name)

        # else we gotta manually query contracts via etherscan
        normal_name = normalize_name(name)
        resolver_addr = self._call_contract_etherscan(
            ENS_MAINNET_ADDR,
            abi=ENS_ABI,
            method_name='resolver',
            arguments=[normal_name_to_hash(normal_name)],
        )
        if is_none_or_zero_address(resolver_addr):
            return None
        address = self._call_contract_etherscan(
            to_checksum_address(resolver_addr),
            abi=ENS_RESOLVER_ABI,
            method_name='addr',
            arguments=[normal_name_to_hash(normal_name)],
        )

        if is_none_or_zero_address(address):
            return None
        return to_checksum_address(address)
Ejemplo n.º 6
0
def _prepare_ens_call_arguments(addr: ChecksumEthAddress) -> List[Any]:
    try:
        reversed_domain = address_to_reverse_domain(addr)
    except (TypeError, ValueError) as e:
        raise InputError(f'Address {addr} has incorrect format or type. {str(e)}') from e
    normalized_domain_name = normalize_name(reversed_domain)
    arguments = [normal_name_to_hash(normalized_domain_name)]
    return arguments
Ejemplo n.º 7
0
    def resolver(self, name: str) -> Optional[Union['Contract', 'AsyncContract']]:
        """
        Get the resolver for an ENS name.

        :param str name: The ENS name
        """
        normal_name = normalize_name(name)
        return self._get_resolver(normal_name)[0]
Ejemplo n.º 8
0
 def _setup_reverse(
     self, name: str, address: ChecksumAddress, transact: "TxParams"={}
 ) -> HexBytes:
     if name:
         name = normalize_name(name)
     else:
         name = ''
     transact['from'] = address
     return self._reverse_registrar().functions.setName(name).transact(transact)
Ejemplo n.º 9
0
 def _setup_reverse(
     self, name: str, address: ChecksumAddress, transact: Optional["TxParams"] = None
 ) -> HexBytes:
     name = normalize_name(name) if name else ''
     if not transact:
         transact = {}
     transact = deepcopy(transact)
     transact['from'] = address
     return self._reverse_registrar().functions.setName(name).transact(transact)
Ejemplo n.º 10
0
 def resolve(self, name, get='addr'):
     normal_name = normalize_name(name)
     resolver = self.resolver(normal_name)
     if resolver:
         lookup_function = getattr(resolver, get)
         namehash = name_to_hash(normal_name)
         return lookup_function(namehash)
     else:
         return None
Ejemplo n.º 11
0
 def resolve(self, name, get='addr'):
     normal_name = normalize_name(name)
     resolver = self.resolver(normal_name)
     if resolver:
         lookup_function = getattr(resolver, get)
         namehash = name_to_hash(normal_name)
         return lookup_function(namehash)
     else:
         return None
Ejemplo n.º 12
0
 def resolve(self, name: str, get: str='addr') -> Optional[Union[ChecksumAddress, str]]:
     normal_name = normalize_name(name)
     resolver = self.resolver(normal_name)
     if resolver:
         lookup_function = getattr(resolver.functions, get)
         namehash = normal_name_to_hash(normal_name)
         address = lookup_function(namehash).call()
         if is_none_or_zero_address(address):
             return None
         return lookup_function(namehash).call()
     else:
         return None
Ejemplo n.º 13
0
 def resolve(self, name, get='addr'):
     normal_name = normalize_name(name)
     resolver = self.resolver(normal_name)
     if resolver:
         lookup_function = getattr(resolver.functions, get)
         namehash = normal_name_to_hash(normal_name)
         address = lookup_function(namehash).call()
         if is_none_or_zero_address(address):
             return None
         return lookup_function(namehash).call()
     else:
         return None
Ejemplo n.º 14
0
    def _ens_lookup(
            self,
            web3: Optional[Web3],
            name: str,
            blockchain: SupportedBlockchain = SupportedBlockchain.ETHEREUM,
    ) -> Optional[Union[ChecksumEthAddress, HexStr]]:
        """Performs an ENS lookup and returns address if found else None

        TODO: currently web3.py 5.15.0 does not support multichain ENS domains
        (EIP-2304), therefore requesting a non-Ethereum address won't use the
        web3 ens library and will require to extend the library resolver ABI.
        An issue in their repo (#1839) reporting the lack of support has been
        created. This function will require refactoring once they include
        support for EIP-2304.
        https://github.com/ethereum/web3.py/issues/1839

        May raise:
        - RemoteError if Etherscan is used and there is a problem querying it or
        parsing its response
        """
        normal_name = normalize_name(name)
        resolver_addr = self._call_contract(
            web3=web3,
            contract_address=ENS_MAINNET_ADDR,
            abi=ENS_ABI,
            method_name='resolver',
            arguments=[normal_name_to_hash(normal_name)],
        )
        if is_none_or_zero_address(resolver_addr):
            return None

        ens_resolver_abi = ENS_RESOLVER_ABI.copy()
        arguments = [normal_name_to_hash(normal_name)]
        if blockchain != SupportedBlockchain.ETHEREUM:
            ens_resolver_abi.extend(ENS_RESOLVER_ABI_MULTICHAIN_ADDRESS)
            arguments.append(blockchain.ens_coin_type())

        address = self._call_contract(
            web3=web3,
            contract_address=to_checksum_address(resolver_addr),
            abi=ens_resolver_abi,
            method_name='addr',
            arguments=arguments,
        )
        if is_none_or_zero_address(address):
            return None

        if blockchain != SupportedBlockchain.ETHEREUM:
            return HexStr(address.hex())

        return to_checksum_address(address)
Ejemplo n.º 15
0
    def _first_owner(self, name):
        """
        Takes a name, and returns the owner of the deepest subdomain that has an owner

        :returns: (owner or None, list(unowned_subdomain_labels), first_owned_domain)
        """
        owner = None
        unowned = []
        pieces = normalize_name(name).split('.')
        while pieces and not owner:
            name = '.'.join(pieces)
            owner = self.owner(name)
            if not owner:
                unowned.append(pieces.pop(0))
        return (owner, unowned, name)
Ejemplo n.º 16
0
    def _first_owner(self, name: str) -> Tuple[Optional[ChecksumAddress], Sequence[str], str]:
        """
        Takes a name, and returns the owner of the deepest subdomain that has an owner

        :returns: (owner or None, list(unowned_subdomain_labels), first_owned_domain)
        """
        owner = None
        unowned = []
        pieces = normalize_name(name).split('.')
        while pieces and is_none_or_zero_address(owner):
            name = '.'.join(pieces)
            owner = self.owner(name)
            if is_none_or_zero_address(owner):
                unowned.append(pieces.pop(0))
        return (owner, unowned, name)
Ejemplo n.º 17
0
    def set_text(
        self,
        name: str,
        key: str,
        value: str,
        transact: "TxParams" = None
    ) -> HexBytes:
        """
        Set the value of a text record of an ENS name.

        :param str name: ENS name
        :param str key: Name of the attribute to set
        :param str value: Value to set the attribute to
        :param dict transact: The transaction configuration, like in
            :meth:`~web3.eth.Eth.send_transaction`
        :return: Transaction hash
        :rtype: HexBytes
        :raises UnsupportedFunction: If the resolver does not support the "0x59d1d43c" interface id
        :raises ResolverNotFound: If no resolver is found for the provided name
        """
        if not transact:
            transact = {}

        owner = self.owner(name)
        node = raw_name_to_hash(name)
        normal_name = normalize_name(name)

        transaction_dict = merge({'from': owner}, transact)

        r = self.resolver(normal_name)
        if r:
            if _resolver_supports_interface(r, GET_TEXT_INTERFACE_ID):
                return r.functions.setText(node, key, value).transact(transaction_dict)
            else:
                raise UnsupportedFunction(
                    f"Resolver for name `{name}` does not support `text` function"
                )
        else:
            raise ResolverNotFound(
                f"No resolver found for name `{name}`. It is likely the name contains an "
                "unsupported top level domain (tld)."
            )
Ejemplo n.º 18
0
 def nameprep(name: str) -> str:
     return normalize_name(name)