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
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)." )
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)
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)
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)
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
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]
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)
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)
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
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
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
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
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)
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)
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)
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)." )
def nameprep(name: str) -> str: return normalize_name(name)