def ens_reverse_lookup(self, reversed_addresses: List[ChecksumEthAddress]) -> Dict[ChecksumEthAddress, Optional[str]]: # noqa: E501 """Performs a reverse ENS lookup on a list of addresses Because a multicall is used, no exceptions are raised. If any exceptions occur, they are logged and None is returned for that """ human_names: Dict[ChecksumEthAddress, Optional[str]] = {} # Querying resolvers' addresses resolver_params = [ EnsContractParams(address=addr, abi=ENS_ABI, method_name='resolver', arguments=_prepare_ens_call_arguments(addr)) # noqa: E501 for addr in reversed_addresses ] resolvers_output = multicall( ethereum=self, calls=[(ENS_MAINNET_ADDR, _encode_ens_contract(params=params)) for params in resolver_params], # noqa: E501 ) resolvers = [] # We need a new list for reversed_addresses because not all addresses have resolver filtered_reversed_addresses = [] # Processing resolvers query output for reversed_addr, params, resolver_output in zip(reversed_addresses, resolver_params, resolvers_output): # noqa: E501 decoded_resolver = _decode_ens_contract(params=params, result_encoded=resolver_output) if is_none_or_zero_address(decoded_resolver): human_names[reversed_addr] = None continue try: deserialized_resolver = deserialize_ethereum_address(decoded_resolver) except DeserializationError: log.error( f'Error deserializing address {decoded_resolver} while doing reverse ens lookup', # noqa: E501 ) human_names[reversed_addr] = None continue resolvers.append(deserialized_resolver) filtered_reversed_addresses.append(reversed_addr) # Querying human names human_names_params = [ EnsContractParams(address=resolver, abi=ENS_RESOLVER_ABI, method_name='name', arguments=_prepare_ens_call_arguments(addr)) # noqa: E501 for addr, resolver in zip(filtered_reversed_addresses, resolvers)] human_names_output = multicall( ethereum=self, calls=[(params.address, _encode_ens_contract(params=params)) for params in human_names_params], # noqa: E501 ) # Processing human names query output for addr, params, human_name_output in zip(filtered_reversed_addresses, human_names_params, human_names_output): # noqa: E501 human_names[addr] = _decode_ens_contract(params=params, result_encoded=human_name_output) # noqa: E501 return human_names
def _get_accounts_proxy( self, addresses: List[ChecksumEthAddress], ) -> Dict[ChecksumEthAddress, ChecksumEthAddress]: """ Returns DSProxy if it exists for a list of addresses using only one call to the chain. May raise: - RemoteError if query to the node failed """ output = multicall( ethereum=self.ethereum, calls=[( DS_PROXY_REGISTRY.address, DS_PROXY_REGISTRY.encode(method_name='proxies', arguments=[address]), ) for address in addresses], ) mapping = {} for idx, result_encoded in enumerate(output): address = addresses[idx] result = DS_PROXY_REGISTRY.decode( # pylint: disable=unsubscriptable-object result_encoded, 'proxies', arguments=[address], )[0] if int(result, 16) != 0: try: proxy_address = deserialize_ethereum_address(result) mapping[address] = proxy_address except DeserializationError as e: msg = f'Failed to deserialize {result} DSproxy for address {address}. {str(e)}' log.error(msg) return mapping
def get_pool_price( self, pool_addr: ChecksumEthAddress, block_identifier: BlockIdentifier = 'latest', ) -> PoolPrice: """ Returns the units of token1 that one token0 can buy """ pool_contract = EthereumContract( address=pool_addr, abi=UNISWAP_V3_POOL_ABI, deployed_block=UNISWAP_FACTORY_DEPLOYED_BLOCK, ) calls = [ ( pool_contract.address, pool_contract.encode(method_name='slot0'), ), ( pool_contract.address, pool_contract.encode(method_name='token0'), ), ( pool_contract.address, pool_contract.encode(method_name='token1'), ), ] output = multicall( ethereum=self.eth_manager, calls=calls, require_success=True, block_identifier=block_identifier, ) token_0 = EthereumToken( to_checksum_address(pool_contract.decode(output[1], 'token0')[0]), # noqa: E501 pylint:disable=unsubscriptable-object ) token_1 = EthereumToken( to_checksum_address(pool_contract.decode(output[2], 'token1')[0]), # noqa: E501 pylint:disable=unsubscriptable-object ) sqrt_price_x96, _, _, _, _, _, _ = pool_contract.decode( output[0], 'slot0') decimals_constant = 10**(token_0.decimals - token_1.decimals) price = FVal( (sqrt_price_x96 * sqrt_price_x96) / 2**(192) * decimals_constant) if ZERO == price: raise DefiPoolError( f'Uniswap pool for {token_0}/{token_1} has price 0') return PoolPrice(price=price, token_0=token_0, token_1=token_1)
def get_pool_price( self, pool_addr: ChecksumEthAddress, block_identifier: BlockIdentifier = 'latest', ) -> PoolPrice: """ Returns the units of token1 that one token0 can buy """ pool_contract = EthereumContract( address=pool_addr, abi=UNISWAP_V2_LP_ABI, deployed_block=10000835, # Factory deployment block ) calls = [ ( pool_contract.address, pool_contract.encode(method_name='getReserves'), ), ( pool_contract.address, pool_contract.encode(method_name='token0'), ), ( pool_contract.address, pool_contract.encode(method_name='token1'), ), ] output = multicall( ethereum=self.eth_manager, calls=calls, require_success=True, block_identifier=block_identifier, ) token_0 = EthereumToken( to_checksum_address(pool_contract.decode(output[1], 'token0')[0]), # noqa: E501 pylint:disable=unsubscriptable-object ) token_1 = EthereumToken( to_checksum_address(pool_contract.decode(output[2], 'token1')[0]), # noqa: E501 pylint:disable=unsubscriptable-object ) reserve_0, reserve_1, _ = pool_contract.decode(output[0], 'getReserves') decimals_constant = 10**(token_0.decimals - token_1.decimals) if ZERO in (reserve_0, reserve_1): raise DefiPoolError( f'Uniswap pool for {token_0}/{token_1} has asset with no reserves' ) price = FVal((reserve_1 / reserve_0) * decimals_constant) return PoolPrice(price=price, token_0=token_0, token_1=token_1)
def _get_vault_details( self, cdp_id: int, ) -> Tuple[ChecksumEthAddress, ChecksumEthAddress]: """ Queries the CDPManager to get the CDP details. Returns a tuple with the CDP address and the CDP owner as of the time of this call. May raise: - RemoteError if query to the node failed - DeserializationError if the query returns unexpected output """ output = multicall( ethereum=self.ethereum, calls=[( MAKERDAO_CDP_MANAGER.address, MAKERDAO_CDP_MANAGER.encode(method_name='urns', arguments=[cdp_id]), ), ( MAKERDAO_CDP_MANAGER.address, MAKERDAO_CDP_MANAGER.encode(method_name='owns', arguments=[cdp_id]), )], ) mapping = {} for result_encoded, method_name in zip(output, ('urns', 'owns')): result = MAKERDAO_CDP_MANAGER.decode( # pylint: disable=unsubscriptable-object result_encoded, method_name, arguments=[cdp_id], )[0] if int(result, 16) == 0: raise DeserializationError( 'Could not deserialize {result} as address}') address = deserialize_ethereum_address(result) mapping[method_name] = address return mapping['urns'], mapping['owns']