Example #1
0
    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
Example #2
0
    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
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
    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']