Exemple #1
0
    def _query_ethereum_tokens(
            self,
            action: AccountAction,
            given_accounts: Optional[List[ChecksumEthAddress]] = None,
            force_detection: bool = False,
    ) -> None:
        """Queries ethereum token balance via either etherscan or ethereum node

        By default queries all accounts but can also be given a specific list of
        accounts to query.

        Should come here during addition of a new account or querying of all token
        balances.

        May raise:
        - RemoteError if an external service such as Etherscan or cryptocompare
        is queried and there is a problem with its query.
        - EthSyncError if querying the token balances through a provided ethereum
        client and the chain is not synced
        """
        if given_accounts is None:
            accounts = self.accounts.eth
        else:
            accounts = given_accounts

        ethtokens = EthTokens(database=self.database, ethereum=self.ethereum)
        try:
            balance_result, token_usd_price = ethtokens.query_tokens_for_addresses(
                addresses=accounts,
                force_detection=force_detection,
            )
        except BadFunctionCallOutput as e:
            log.error(
                'Assuming unsynced chain. Got web3 BadFunctionCallOutput '
                'exception: {}'.format(str(e)),
            )
            raise EthSyncError(
                'Tried to use the ethereum chain of the provided client to query '
                'token balances but the chain is not synced.',
            ) from e

        self._update_balances_after_token_query(action, balance_result, token_usd_price)  # noqa: E501
Exemple #2
0
def ethtokens(ethereum_manager, database):
    return EthTokens(database, ethereum_manager)
Exemple #3
0
    def _query_ethereum_tokens(
        self,
        action: AccountAction,
        given_accounts: Optional[List[ChecksumEthAddress]] = None,
        force_detection: bool = False,
    ) -> None:
        """Queries ethereum token balance via either etherscan or ethereum node

        By default queries all accounts but can also be given a specific list of
        accounts to query.

        Should come here during addition of a new account or querying of all token
        balances.

        May raise:
        - RemoteError if an external service such as Etherscan or cryptocompare
        is queried and there is a problem with its query.
        - EthSyncError if querying the token balances through a provided ethereum
        client and the chain is not synced
        """
        if given_accounts is None:
            accounts = self.accounts.eth
        else:
            accounts = given_accounts

        ethtokens = EthTokens(database=self.database, ethereum=self.ethereum)
        try:
            balance_result, token_usd_price = ethtokens.query_tokens_for_addresses(
                addresses=accounts,
                force_detection=force_detection,
            )
        except BadFunctionCallOutput as e:
            log.error(
                'Assuming unsynced chain. Got web3 BadFunctionCallOutput '
                'exception: {}'.format(str(e)), )
            raise EthSyncError(
                'Tried to use the ethereum chain of the provided client to query '
                'token balances but the chain is not synced.', )

        # Update the per account token balance and usd value
        token_totals: Dict[EthereumToken, FVal] = defaultdict(FVal)
        eth_balances = self.balances.eth
        for account, token_balances in balance_result.items():
            for token, token_balance in token_balances.items():
                if token_usd_price[token] == ZERO:
                    # skip tokens that have no price
                    continue

                token_totals[token] += token_balance
                usd_value = token_balance * token_usd_price[token]
                eth_balances[account].assets[token] = Balance(
                    amount=token_balance,
                    usd_value=usd_value,
                )

        # Update the totals
        for token, token_total_balance in token_totals.items():
            if action == AccountAction.QUERY:
                self.totals.assets[token] = Balance(
                    amount=token_total_balance,
                    usd_value=token_total_balance * token_usd_price[token],
                )
            else:  # addition
                self.totals.assets[token] += Balance(
                    amount=token_total_balance,
                    usd_value=token_total_balance * token_usd_price[token],
                )
Exemple #4
0
    def _query_ethereum_tokens(
        self,
        action: AccountAction,
        given_accounts: Optional[List[ChecksumEthAddress]] = None,
        force_detection: bool = False,
    ) -> None:
        """Queries ethereum token balance via either etherscan or ethereum node

        By default queries all accounts but can also be given a specific list of
        accounts to query.

        May raise:
        - RemoteError if an external service such as Etherscan or cryptocompare
        is queried and there is a problem with its query.
        - EthSyncError if querying the token balances through a provided ethereum
        client and the chain is not synced
        """
        if given_accounts is None:
            accounts = self.accounts.eth
        else:
            accounts = given_accounts

        ethtokens = EthTokens(database=self.database, ethereum=self.ethereum)
        try:
            balance_result, token_usd_price = ethtokens.query_tokens_for_addresses(
                addresses=accounts,
                force_detection=force_detection,
            )
        except BadFunctionCallOutput as e:
            log.error(
                'Assuming unsynced chain. Got web3 BadFunctionCallOutput '
                'exception: {}'.format(str(e)), )
            raise EthSyncError(
                'Tried to use the ethereum chain of the provided client to query '
                'token balances but the chain is not synced.', )

        add_or_sub: Optional[Callable[[Any, Any], Any]]
        if action == AccountAction.APPEND:
            add_or_sub = operator.add
        elif action == AccountAction.REMOVE:
            add_or_sub = operator.sub
        else:
            add_or_sub = None

        # Update the per account token balance and usd value
        token_totals: Dict[EthereumToken, FVal] = defaultdict(FVal)
        eth_balances = self.balances.eth
        for account, token_balances in balance_result.items():
            for token, token_balance in token_balances.items():
                if token_usd_price[token] == ZERO:
                    # skip tokens that have no price
                    continue

                token_totals[token] += token_balance
                if action == AccountAction.QUERY or action == AccountAction.APPEND:
                    usd_value = token_balance * token_usd_price[token]
                    eth_balances[account].asset_balances[token] = Balance(
                        amount=token_balance,
                        usd_value=usd_value,
                    )
                    eth_balances[account].increase_total_usd_value(usd_value)

        # Update the totals
        for token, token_total_balance in token_totals.items():
            if action == AccountAction.QUERY:
                self.totals[token] = Balance(
                    amount=token_total_balance,
                    usd_value=token_total_balance * token_usd_price[token],
                )
            else:
                if action == AccountAction.REMOVE and token not in self.totals:
                    # Removing the only account that holds this token
                    self.totals[token] = Balance(amount=ZERO, usd_value=ZERO)
                else:
                    new_amount = add_or_sub(
                        self.totals[token].amount,
                        token_total_balance)  # type: ignore  # noqa: E501
                    if new_amount <= ZERO:
                        new_amount = ZERO
                        new_usd_value = ZERO
                    else:
                        new_usd_value = add_or_sub(  # type: ignore
                            self.totals[token].usd_value,
                            token_total_balance * token_usd_price[token],
                        )
                    self.totals[token] = Balance(
                        amount=new_amount,
                        usd_value=new_usd_value,
                    )
Exemple #5
0
    def _add_protocol_balances(self) -> None:
        """Also count token balances that may come from various protocols"""
        # If we have anything in DSR also count it towards total blockchain balances
        eth_balances = self.balances.eth
        dsr_module = self.makerdao_dsr
        if dsr_module is not None:
            additional_total = Balance()
            current_dsr_report = dsr_module.get_current_dsr()
            for dsr_account, balance_entry in current_dsr_report.balances.items():

                if balance_entry.amount == ZERO:
                    continue

                eth_balances[dsr_account].assets[A_DAI] += balance_entry
                additional_total += balance_entry

            if additional_total.amount != ZERO:
                self.totals.assets[A_DAI] += additional_total

        # Also count the vault balance and defi saver wallets and add it to the totals
        vaults_module = self.makerdao_vaults
        if vaults_module is not None:
            balances = vaults_module.get_balances()
            for address, entry in balances.items():
                if address not in eth_balances:
                    self.msg_aggregator.add_error(
                        f'The owner of a vault {address} was not in the tracked addresses.'
                        f' This should not happen and is probably a bug. Please report it.',
                    )
                else:
                    eth_balances[address] += entry
                    self.totals += entry

            proxy_mappings = vaults_module._get_accounts_having_maker_proxy()
            proxy_to_address = {}
            proxy_addresses = []
            for user_address, proxy_address in proxy_mappings.items():
                proxy_to_address[proxy_address] = user_address
                proxy_addresses.append(proxy_address)

            ethtokens = EthTokens(database=self.database, ethereum=self.ethereum)
            try:
                balance_result, token_usd_price = ethtokens.query_tokens_for_addresses(
                    addresses=proxy_addresses,
                    force_detection=False,
                )
            except BadFunctionCallOutput as e:
                log.error(
                    'Assuming unsynced chain. Got web3 BadFunctionCallOutput '
                    'exception: {}'.format(str(e)),
                )
                raise EthSyncError(
                    'Tried to use the ethereum chain of the provided client to query '
                    'token balances but the chain is not synced.',
                ) from e

            new_result = {proxy_to_address[x]: v for x, v in balance_result.items()}
            self._update_balances_after_token_query(
                action=AccountAction.DSR_PROXY_APPEND,
                balance_result=new_result,
                token_usd_price=token_usd_price,
            )

            # also query defi balances to get liabilities
            defi_balances_map = self.defichad.query_defi_balances(proxy_addresses)
            for proxy_address, defi_balances in defi_balances_map.items():
                self._add_account_defi_balances_to_token_and_totals(
                    account=proxy_to_address[proxy_address],
                    balances=defi_balances,
                )

        adex_module = self.adex
        if adex_module is not None and self.premium is not None:
            adex_balances = adex_module.get_balances(addresses=self.accounts.eth)
            for address, pool_balances in adex_balances.items():
                for pool_balance in pool_balances:
                    eth_balances[address].assets[A_ADX] += pool_balance.adx_balance
                    self.totals.assets[A_ADX] += pool_balance.adx_balance
                    eth_balances[address].assets[A_DAI] += pool_balance.dai_unclaimed_balance
                    self.totals.assets[A_DAI] += pool_balance.dai_unclaimed_balance

        # Count ETH staked in Eth2 beacon chain
        self.account_for_staked_eth2_balances(addresses=self.accounts.eth, at_addition=False)
        # Finally count the balances detected in various protocols in defi balances
        self.add_defi_balances_to_token_and_totals()