Beispiel #1
0
    def _get_single_balance(
        self,
        protocol_name: str,
        entry: Tuple[Tuple[str, str, str, int], int],
    ) -> DefiBalance:
        metadata = entry[0]
        balance_value = entry[1]
        decimals = metadata[3]
        normalized_value = token_normalized_value_decimals(
            balance_value, decimals)
        token_symbol = metadata[2]
        token_address = to_checksum_address(metadata[0])
        token_name = metadata[1]

        special_handling = self.handle_protocols(
            protocol_name=protocol_name,
            token_symbol=token_symbol,
            normalized_balance=normalized_value,
            token_address=token_address,
            token_name=token_name,
        )
        if special_handling:
            return special_handling

        try:
            asset = Asset(token_symbol)
            usd_price = Inquirer().find_usd_price(asset)
        except (UnknownAsset, UnsupportedAsset):
            if not _is_token_non_standard(token_symbol, token_address):
                self.msg_aggregator.add_warning(
                    f'Unsupported asset {token_symbol} with address '
                    f'{token_address} encountered during DeFi protocol queries',
                )
            usd_price = Price(ZERO)

        usd_value = normalized_value * usd_price
        defi_balance = DefiBalance(
            token_address=token_address,
            token_name=token_name,
            token_symbol=token_symbol,
            balance=Balance(amount=normalized_value, usd_value=usd_value),
        )
        return defi_balance
Beispiel #2
0
    def remove_eth_tokens(
            self, tokens: List[EthereumToken]) -> BlockchainBalancesUpdate:
        for token in tokens:
            usd_price = Inquirer().find_usd_price(token)
            for account, account_data in self.balances[A_ETH].items():
                if token not in account_data:
                    continue

                balance = account_data[token]
                deleting_usd_value = balance * usd_price
                del self.balances[A_ETH][account][token]
                self.balances[A_ETH][account]['usd_value'] = (
                    self.balances[A_ETH][account]['usd_value'] -
                    deleting_usd_value)

            del self.totals[token]
            self.owned_eth_tokens.remove(token)

        return {'per_account': self.balances, 'totals': self.totals}
Beispiel #3
0
    def query_btc_balances(self) -> None:
        if len(self.accounts.btc) == 0:
            return

        self.balances[A_BTC] = {}
        btc_usd_price = Inquirer().find_usd_price(A_BTC)
        total = FVal(0)
        for account in self.accounts.btc:
            balance = self.query_btc_account_balance(account)
            total += balance
            self.balances[A_BTC][account] = {
                'amount': balance,
                'usd_value': balance * btc_usd_price,
            }

        self.totals[A_BTC] = {
            'amount': total,
            'usd_value': total * btc_usd_price
        }
Beispiel #4
0
def create_inquirer(
    data_directory,
    should_mock_current_price_queries,
    mocked_prices,
    current_price_oracles_order,
) -> Inquirer:
    # Since this is a singleton and we want it initialized everytime the fixture
    # is called make sure its instance is always starting from scratch
    Inquirer._Inquirer__instance = None  # type: ignore
    # Get a cryptocompare without a DB since invoking DB fixture here causes problems
    # of existing user for some tests
    cryptocompare = Cryptocompare(data_directory=data_directory, database=None)
    gecko = Coingecko(data_directory=data_directory)
    inquirer = Inquirer(
        data_dir=data_directory,
        cryptocompare=cryptocompare,
        coingecko=gecko,
    )
    inquirer.set_oracles_order(current_price_oracles_order)

    if not should_mock_current_price_queries:
        return inquirer

    def mock_find_price(
            from_asset,
            to_asset,
            ignore_cache: bool = False,  # pylint: disable=unused-argument
    ):
        return mocked_prices.get((from_asset, to_asset), FVal('1.5'))

    inquirer.find_price = mock_find_price  # type: ignore

    def mock_find_usd_price(asset, ignore_cache: bool = False):  # pylint: disable=unused-argument
        return mocked_prices.get(asset, FVal('1.5'))

    inquirer.find_usd_price = mock_find_usd_price  # type: ignore

    def mock_query_fiat_pair(base, quote):  # pylint: disable=unused-argument
        return FVal(1)

    inquirer._query_fiat_pair = mock_query_fiat_pair  # type: ignore

    return inquirer
Beispiel #5
0
    def set_settings(self, settings):
        log.info('Add new settings')

        message = ''
        with self.lock:
            if 'eth_rpc_endpoint' in settings:
                result, msg = self.blockchain.set_eth_rpc_endpoint(settings['eth_rpc_endpoint'])
                if not result:
                    # Don't save it in the DB
                    del settings['eth_rpc_endpoint']
                    message += "\nEthereum RPC endpoint not set: " + msg

            if 'main_currency' in settings:
                given_symbol = settings['main_currency']
                try:
                    main_currency = Asset(given_symbol)
                except UnknownAsset:
                    return False, f'Unknown fiat currency {given_symbol} provided'

                if not main_currency.is_fiat():
                    msg = (
                        f'Provided symbol for main currency {given_symbol} is '
                        f'not a fiat currency'
                    )
                    return False, msg

                if main_currency != A_USD:
                    self.usd_to_main_currency_rate = Inquirer().query_fiat_pair(
                        A_USD,
                        main_currency,
                    )

            res, msg = self.accountant.customize(settings)
            if not res:
                message += '\n' + msg
                return False, message

            _, msg, = self.data.set_settings(settings, self.accountant)
            if msg != '':
                message += '\n' + msg

            # Always return success here but with a message
            return True, message
Beispiel #6
0
    def unlock_user(self, user, password, create_new, sync_approval, api_key, api_secret):
        # unlock or create the DB
        self.password = password
        user_dir = self.data.unlock(user, password, create_new)
        self.try_premium_at_start(api_key, api_secret, create_new, sync_approval, user_dir)

        secret_data = self.data.db.get_exchange_secrets()
        settings = self.data.db.get_settings()
        historical_data_start = settings['historical_data_start']
        eth_rpc_port = settings['eth_rpc_port']
        self.trades_historian = TradesHistorian(
            self.data_dir,
            self.data.db,
            self.data.get_eth_accounts(),
            historical_data_start,
        )
        self.price_historian = PriceHistorian(
            self.data_dir,
            historical_data_start,
        )
        db_settings = self.data.db.get_settings()
        self.accountant = Accountant(
            price_historian=self.price_historian,
            profit_currency=self.data.main_currency(),
            user_directory=user_dir,
            create_csv=True,
            ignored_assets=self.data.db.get_ignored_assets(),
            include_crypto2crypto=db_settings['include_crypto2crypto'],
            taxfree_after_period=db_settings['taxfree_after_period'],
        )

        self.inquirer = Inquirer(kraken=self.kraken)
        self.initialize_exchanges(secret_data)

        ethchain = Ethchain(eth_rpc_port)
        self.blockchain = Blockchain(
            blockchain_accounts=self.data.db.get_blockchain_accounts(),
            all_eth_tokens=self.data.eth_tokens,
            owned_eth_tokens=self.data.db.get_owned_tokens(),
            inquirer=self.inquirer,
            ethchain=ethchain,
        )
Beispiel #7
0
    def get_nfts_with_price(self) -> List[Dict[str, Any]]:
        cursor = self.db.conn.cursor()
        query = cursor.execute(
            'SELECT identifier, last_price, last_price_asset, manual_price from nfts WHERE last_price IS NOT NULL'
        )  # noqa: E501
        result = []
        for entry in query:
            to_asset_id = entry[2] if entry[2] is not None else A_USD
            try:
                to_asset = Asset(to_asset_id)
            except UnknownAsset:
                log.error(
                    f'Unknown asset {to_asset_id} in custom nft price DB table. Ignoring.'
                )
                continue

            if to_asset != A_USD:
                try:
                    to_asset_usd_price = Inquirer().find_usd_price(to_asset)
                except RemoteError as e:
                    log.error(
                        f'Error querying current usd price of {to_asset} in custom nft price '
                        f'api call due to {str(e)}. Ignoring.', )
                    continue
                if to_asset_usd_price == ZERO:
                    log.error(
                        f'Could not find current usd price for {to_asset} in custom nft '
                        f'price api call. Ignoring.', )
                    continue
                usd_price = to_asset_usd_price * FVal(entry[1])
            else:  # to_asset == USD
                usd_price = entry[1]

            result.append({
                'asset': entry[0],
                'manually_input': bool(entry[3]),
                'price_asset': to_asset_id,
                'price_in_asset': entry[1],
                'usd_price': str(usd_price),
            })

        return result
Beispiel #8
0
    def query_ethereum_balances(self) -> None:
        """Queries the ethereum balances and populates the state

        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 len(self.accounts.eth) == 0:
            return

        eth_accounts = self.accounts.eth
        eth_usd_price = Inquirer().find_usd_price(A_ETH)
        balances = self.ethchain.get_multieth_balance(eth_accounts)
        eth_total = FVal(0)
        eth_balances: EthBalances = {}
        for account, balance in balances.items():
            eth_total += balance
            usd_value = balance * eth_usd_price
            eth_balances[account] = {
                'assets': {
                    A_ETH: {
                        'amount': balance,
                        'usd_value': usd_value
                    },
                },
                'total_usd_value': usd_value,
            }

        self.totals[A_ETH] = {
            'amount': eth_total,
            'usd_value': eth_total * eth_usd_price
        }
        # but they are not complete until token query
        self.balances[A_ETH] = cast(
            Dict[BlockchainAddress, Dict[Union[str, Asset], FVal]],
            eth_balances,
        )

        # And now for tokens
        self.query_ethereum_tokens(self.owned_eth_tokens, eth_balances)
Beispiel #9
0
    def modify_btc_account(
            self,
            account: BTCAddress,
            append_or_remove: str,
            add_or_sub: Callable[[FVal, FVal], FVal],
    ) -> None:
        """Either appends or removes a BTC acccount.

        Call with 'append', operator.add to add the account
        Call with 'remove', operator.sub to remove the account

        May raise:
        - RemotError if there is a problem querying blockchain.info or cryptocompare
        """
        btc_usd_price = Inquirer().find_usd_price(A_BTC)
        remove_with_populated_balance = (
            append_or_remove == 'remove' and len(self.balances.btc) != 0
        )
        # Query the balance of the account except for the case when it's removed
        # and there is no other account in the balances
        if append_or_remove == 'append' or remove_with_populated_balance:
            balance = self.query_btc_account_balance(account)
            usd_balance = balance * btc_usd_price

        if append_or_remove == 'append':
            self.balances.btc[account] = Balance(amount=balance, usd_value=usd_balance)
        elif append_or_remove == 'remove':
            if account in self.balances.btc:
                del self.balances.btc[account]
        else:
            raise AssertionError('Programmer error: Should be append or remove')

        if len(self.balances.btc) == 0:
            # If the last account was removed balance should be 0
            self.totals[A_BTC].amount = ZERO
            self.totals[A_BTC].usd_value = ZERO
        else:
            self.totals[A_BTC].amount = add_or_sub(self.totals[A_BTC].amount, balance)
            self.totals[A_BTC].usd_value = add_or_sub(self.totals[A_BTC].usd_value, usd_balance)

        # At the very end add/remove it from the accounts
        getattr(self.accounts.btc, append_or_remove)(account)
Beispiel #10
0
    def _get_known_asset_price(
        known_assets: Set[EthereumToken],
        unknown_assets: Set[EthereumToken],
    ) -> AssetToPrice:
        """Get the tokens prices via Inquirer

        Given an asset, if `find_usd_price()` returns zero, it will be added
        into `unknown_assets`.
        """
        asset_price: AssetToPrice = {}

        for known_asset in known_assets:
            asset_usd_price = Inquirer().find_usd_price(known_asset)

            if asset_usd_price != Price(ZERO):
                asset_price[known_asset.ethereum_address] = asset_usd_price
            else:
                unknown_assets.add(known_asset)

        return asset_price
Beispiel #11
0
    def query_btc_balances(self) -> None:
        """Queries blockchain.info/blockcypher for the balance of all BTC accounts

        May raise:
        - RemotError if there is a problem querying blockchain.info or cryptocompare
        """
        if len(self.accounts.btc) == 0:
            return

        self.balances.btc = {}
        btc_usd_price = Inquirer().find_usd_price(A_BTC)
        total = FVal(0)
        balances = get_bitcoin_addresses_balances(self.accounts.btc)
        for account, balance in balances.items():
            total += balance
            self.balances.btc[account] = Balance(
                amount=balance,
                usd_value=balance * btc_usd_price,
            )
        self.totals[A_BTC] = Balance(amount=total, usd_value=total * btc_usd_price)
Beispiel #12
0
 def on_account_addition(
         self, address: ChecksumEthAddress) -> Optional[List[AssetBalance]]:
     """When an account is added for adex check its balances"""
     balance = self.staking_pool.call(self.ethereum,
                                      'balanceOf',
                                      arguments=[address])
     if balance == 0:
         return None
     # else the address has staked adex
     usd_price = Inquirer().find_usd_price(A_ADX)
     share_price = self.staking_pool.call(self.ethereum, 'shareValue')
     amount = token_normalized_value_decimals(
         token_amount=balance * share_price / (FVal(10)**18),
         token_decimals=18,
     )
     return [
         AssetBalance(asset=A_ADX,
                      balance=Balance(amount=amount,
                                      usd_value=amount * usd_price))
     ]  # noqa: E501
Beispiel #13
0
def get_manually_tracked_balances(db: 'DBHandler') -> List[ManuallyTrackedBalanceWithValue]:
    """Gets the manually tracked balances"""
    balances = db.get_manually_tracked_balances()
    balances_with_value = []
    for entry in balances:
        try:
            price = Inquirer().find_usd_price(entry.asset)
        except RemoteError as e:
            db.msg_aggregator.add_warning(
                f'Could not find price for {entry.asset.identifier} during '
                f'manually tracked balance querying due to {str(e)}',
            )
            price = Price(ZERO)
        # https://github.com/python/mypy/issues/2582 --> for the type ignore below
        balances_with_value.append(ManuallyTrackedBalanceWithValue(  # type: ignore
            **entry._asdict(),
            usd_value=price * entry.amount,
        ))

    return balances_with_value
Beispiel #14
0
    def query_historical_price(from_asset: Asset, to_asset: Asset,
                               timestamp: Timestamp) -> Price:
        """
        Query the historical price on `timestamp` for `from_asset` in `to_asset`.
        So how much `to_asset` does 1 unit of `from_asset` cost.

        Args:
            from_asset: The ticker symbol of the asset for which we want to know
                        the price.
            to_asset: The ticker symbol of the asset against which we want to
                      know the price.
            timestamp: The timestamp at which to query the price
        """
        log.debug(
            'Querying historical price',
            from_asset=from_asset,
            to_asset=to_asset,
            timestamp=timestamp,
        )

        if from_asset == to_asset:
            return Price(FVal('1'))

        if from_asset.is_fiat() and to_asset.is_fiat():
            # if we are querying historical forex data then try something other than cryptocompare
            price = Inquirer().query_historical_fiat_exchange_rates(
                from_fiat_currency=FiatAsset(from_asset.identifier),
                to_fiat_currency=FiatAsset(to_asset.identifier),
                timestamp=timestamp,
            )
            if price is not None:
                return price
            # else cryptocompare also has historical fiat to fiat data

        instance = PriceHistorian()
        return instance._cryptocompare.query_historical_price(
            from_asset=from_asset,
            to_asset=to_asset,
            timestamp=timestamp,
            historical_data_start=instance._historical_data_start,
        )
Beispiel #15
0
    def get_current_dsr(self) -> DSRCurrentBalances:
        """Gets the current DSR balance for all accounts that have DAI in DSR
        and the current DSR percentage

        May raise:
        - RemoteError if etherscan is used and there is a problem with
        reaching it or with the returned result.
        - BlockchainQueryError if an ethereum node is used and the contract call
        queries fail for some reason
        """
        with self.lock:
            proxy_mappings = self._get_accounts_having_maker_proxy()
            balances = {}
            try:
                current_dai_price = Inquirer().find_usd_price(
                    EthereumToken('DAI'))
            except RemoteError:
                current_dai_price = Price(FVal(1))
            for account, proxy in proxy_mappings.items():
                guy_slice = MAKERDAO_POT.call(self.ethereum,
                                              'pie',
                                              arguments=[proxy])
                if guy_slice == 0:
                    # no current DSR balance for this proxy
                    continue
                chi = MAKERDAO_POT.call(self.ethereum, 'chi')
                dai_balance = _dsrdai_to_dai(guy_slice * chi)
                balances[account] = Balance(
                    amount=dai_balance,
                    usd_value=current_dai_price * dai_balance,
                )

            current_dsr = MAKERDAO_POT.call(self.ethereum, 'dsr')
            # Calculation is from here:
            # https://docs.makerdao.com/smart-contract-modules/rates-module#a-note-on-setting-rates
            current_dsr_percentage = (
                (FVal(current_dsr / RAY)**31622400) % 1) * 100
            result = DSRCurrentBalances(balances=balances,
                                        current_dsr=current_dsr_percentage)

        return result
Beispiel #16
0
    def query_balances(
            self) -> Tuple[Optional[Dict[Asset, Dict[str, Any]]], str]:
        try:
            resp = self.api_query('getbalances')
        except RemoteError as e:
            msg = ('Bittrex API request failed. Could not reach bittrex due '
                   'to {}'.format(e))
            log.error(msg)
            return None, msg

        returned_balances = dict()
        for entry in resp:
            try:
                asset = asset_from_bittrex(entry['Currency'])
            except UnsupportedAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found unsupported bittrex asset {e.asset_name}. '
                    f' Ignoring its balance query.', )
                continue
            except UnknownAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found unknown bittrex asset {e.asset_name}. '
                    f' Ignoring its balance query.', )
                continue

            usd_price = Inquirer().find_usd_price(asset=asset)

            balance = dict()
            balance['amount'] = FVal(entry['Balance'])
            balance['usd_value'] = FVal(balance['amount']) * usd_price
            returned_balances[asset] = balance

            log.debug(
                'bittrex balance query result',
                sensitive_log=True,
                currency=asset,
                amount=balance['amount'],
                usd_value=balance['usd_value'],
            )

        return returned_balances, ''
Beispiel #17
0
    def __init__(self, args: argparse.Namespace) -> None:
        """Initialize the Rotkehlchen object

        May Raise:
        - SystemPermissionError if the given data directory's permissions
        are not correct.
        """
        self.lock = Semaphore()
        self.lock.acquire()

        # Can also be None after unlock if premium credentials did not
        # authenticate or premium server temporarily offline
        self.premium: Optional[Premium] = None
        self.user_is_logged_in: bool = False
        configure_logging(args)

        self.sleep_secs = args.sleep_secs
        if args.data_dir is None:
            self.data_dir = default_data_directory()
        else:
            self.data_dir = Path(args.data_dir)

        if not os.access(self.data_dir, os.W_OK | os.R_OK):
            raise SystemPermissionError(
                f'The given data directory {self.data_dir} is not readable or writable',
            )
        self.args = args
        self.msg_aggregator = MessagesAggregator()
        self.greenlet_manager = GreenletManager(
            msg_aggregator=self.msg_aggregator)
        self.exchange_manager = ExchangeManager(
            msg_aggregator=self.msg_aggregator)
        self.data = DataHandler(self.data_dir, self.msg_aggregator)
        self.cryptocompare = Cryptocompare(data_directory=self.data_dir,
                                           database=None)
        # Initialize the Inquirer singleton
        Inquirer(data_dir=self.data_dir, cryptocompare=self.cryptocompare)

        self.lock.release()
        self.shutdown_event = gevent.event.Event()
Beispiel #18
0
        def process_pool_asset(asset_name: str, asset_amount: FVal) -> None:
            if asset_amount == ZERO:
                return None

            try:
                asset = asset_from_binance(asset_name)
            except UnsupportedAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found unsupported {self.name} asset {asset_name}. '
                    f'Ignoring its {self.name} pool balance query. {str(e)}',
                )
                return None
            except UnknownAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found unknown {self.name} asset {asset_name}. '
                    f'Ignoring its {self.name} pool balance query. {str(e)}',
                )
                return None
            except DeserializationError as e:
                self.msg_aggregator.add_error(
                    f'{self.name} balance deserialization error '
                    f'for asset {asset_name}: {str(e)}. Skipping entry.',
                )
                return None

            try:
                usd_price = Inquirer().find_usd_price(asset)
            except RemoteError as e:
                self.msg_aggregator.add_error(
                    f'Error processing {self.name} balance entry due to inability to '
                    f'query USD price: {str(e)}. Skipping {self.name} pool balance entry',
                )
                return None

            balances[asset] += Balance(
                amount=asset_amount,
                usd_value=asset_amount * usd_price,
            )
            return None
Beispiel #19
0
    def query_btc_balances(self) -> None:
        """Queries blockchain.info for the balance of all BTC accounts

        May raise:
        - RemotError if there is a problem querying blockchain.info or cryptocompare
        """
        if len(self.accounts.btc) == 0:
            return

        self.balances.btc = {}
        btc_usd_price = Inquirer().find_usd_price(A_BTC)
        total = FVal(0)
        for account in self.accounts.btc:
            balance = self.query_btc_account_balance(account)

            total += balance
            self.balances.btc[account] = Balance(
                amount=balance,
                usd_value=balance * btc_usd_price,
            )

        self.totals[A_BTC] = Balance(amount=total, usd_value=total * btc_usd_price)
Beispiel #20
0
    def remove_eth_tokens(self, tokens: List[EthereumToken]) -> BlockchainBalancesUpdate:
        for token in tokens:
            usd_price = Inquirer().find_usd_price(token)
            for account, account_data in self.balances[A_ETH].items():
                if token not in account_data:
                    continue

                balance = account_data[token]
                deleting_usd_value = balance * usd_price
                del self.balances[A_ETH][account][token]
                self.balances[A_ETH][account]['usd_value'] = (
                    self.balances[A_ETH][account]['usd_value'] -
                    deleting_usd_value
                )
            # Remove the token from the totals iff existing. May not exist
            #  if the token price is 0 but is still tracked.
            # See https://github.com/rotkehlchenio/rotkehlchen/issues/467
            # for more details
            self.totals.pop(token, None)
            self.owned_eth_tokens.remove(token)

        return {'per_account': self.balances, 'totals': self.totals}
Beispiel #21
0
    def unlock_user(self, user, password, create_new, sync_approval, api_key,
                    api_secret):
        # unlock or create the DB
        self.password = password
        user_dir = self.data.unlock(user, password, create_new)
        self.try_premium_at_start(api_key, api_secret, create_new,
                                  sync_approval, user_dir)

        secret_data = self.data.db.get_exchange_secrets()
        self.cache_data_filename = os.path.join(self.data_dir,
                                                'cache_data.json')
        settings = self.data.db.get_settings()
        historical_data_start = settings['historical_data_start']
        eth_rpc_port = settings['eth_rpc_port']
        self.trades_historian = TradesHistorian(
            self.data_dir,
            self.data.db,
            self.data.get_eth_accounts(),
            historical_data_start,
        )
        self.price_historian = PriceHistorian(
            self.data_dir,
            historical_data_start,
        )
        self.accountant = Accountant(
            price_historian=self.price_historian,
            profit_currency=self.data.main_currency(),
            user_directory=user_dir,
            create_csv=True,
            ignored_assets=self.data.db.get_ignored_assets())

        self.inquirer = Inquirer(kraken=self.kraken)
        self.initialize_exchanges(secret_data)

        ethchain = Ethchain(eth_rpc_port)
        self.blockchain = Blockchain(self.data.db.get_blockchain_accounts(),
                                     self.data.eth_tokens,
                                     self.data.db.get_owned_tokens(),
                                     self.inquirer, ethchain)
Beispiel #22
0
    def remove_eth_tokens(
            self, tokens: List[EthereumToken]) -> BlockchainBalancesUpdate:
        """
        Removes tokens from the state and stops their balance from being tracked
        for each account

        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 self.balances[A_ETH] == {}:
            # if balances have not been yet queried then we should do the entire
            # balance query first in order to create the eth_balances mappings
            self.query_ethereum_balances()

        for token in tokens:
            usd_price = Inquirer().find_usd_price(token)
            for account, account_data in self.balances[A_ETH].items():
                if token not in account_data['assets']:  # type: ignore
                    continue

                balance = account_data['assets'][token][
                    'amount']  # type: ignore
                deleting_usd_value = balance * usd_price
                del self.balances[A_ETH][account]['assets'][
                    token]  # type: ignore
                self.balances[A_ETH][account]['total_usd_value'] = (
                    self.balances[A_ETH][account]['total_usd_value'] -
                    deleting_usd_value)
            # Remove the token from the totals iff existing. May not exist
            # if the token price is 0 but is still tracked.
            # See https://github.com/rotki/rotki/issues/467
            # for more details
            self.totals.pop(token, None)
            self.owned_eth_tokens.remove(token)

        return {'per_account': self.balances, 'totals': self.totals}
Beispiel #23
0
    def query_balances(self) -> Tuple[Optional[dict], str]:
        try:
            resp = self.api_query('returnCompleteBalances', {"account": "all"})
            # We know returnCompleteBalances returns a dict
            resp = cast(Dict, resp)
        except (RemoteError, PoloniexError) as e:
            msg = ('Poloniex API request failed. Could not reach poloniex due '
                   'to {}'.format(e))
            log.error(msg)
            return None, msg

        balances = dict()
        for poloniex_asset, v in resp.items():
            available = FVal(v['available'])
            on_orders = FVal(v['onOrders'])
            if (available != FVal(0) or on_orders != FVal(0)):
                try:
                    asset = asset_from_poloniex(poloniex_asset)
                except UnsupportedAsset as e:
                    self.msg_aggregator.add_warning(
                        f'Found unsupported poloniex asset {e.asset_name}. '
                        f' Ignoring its balance query.', )
                    continue
                entry = {}
                entry['amount'] = available + on_orders
                usd_price = Inquirer().find_usd_price(asset=asset)
                usd_value = entry['amount'] * usd_price
                entry['usd_value'] = usd_value
                balances[asset] = entry

                log.debug(
                    'Poloniex balance query',
                    sensitive_log=True,
                    currency=asset,
                    amount=entry['amount'],
                    usd_value=usd_value,
                )

        return balances, ''
Beispiel #24
0
    def query_ethereum_balances(self) -> None:
        if len(self.accounts.eth) == 0:
            return

        eth_accounts = self.accounts.eth
        eth_usd_price = Inquirer().find_usd_price(A_ETH)
        balances = self.ethchain.get_multieth_balance(eth_accounts)
        eth_total = FVal(0)
        eth_balances: EthBalances = {}
        for account, balance in balances.items():
            eth_total += balance
            eth_balances[account] = {A_ETH: balance, 'usd_value': balance * eth_usd_price}

        self.totals[A_ETH] = {'amount': eth_total, 'usd_value': eth_total * eth_usd_price}
        # but they are not complete until token query
        self.balances[A_ETH] = cast(
            Dict[BlockchainAddress, Dict[Union[str, Asset], FVal]],
            eth_balances,
        )

        # And now for tokens
        self.query_ethereum_tokens(self.owned_eth_tokens, eth_balances)
Beispiel #25
0
    def query_balances(self, **kwargs: Any) -> ExchangeQueryBalances:
        assets_balance: Dict[Asset, Balance] = {}
        try:
            resp_info = self._api_query('get', 'account')
        except RemoteError as e:
            msg = (
                'Bitcoin.de request failed. Could not reach bitcoin.de due '
                'to {}'.format(e)
            )
            log.error(msg)
            return None, msg

        log.debug(f'Bitcoin.de account response: {resp_info}')
        for currency, balance in resp_info['data']['balances'].items():
            asset = bitcoinde_asset(currency)
            try:
                usd_price = Inquirer().find_usd_price(asset=asset)
            except RemoteError as e:
                self.msg_aggregator.add_error(
                    f'Error processing Bitcoin.de balance entry due to inability to '
                    f'query USD price: {str(e)}. Skipping balance entry',
                )
                continue

            try:
                amount = deserialize_asset_amount(balance['total_amount'])
            except DeserializationError as e:
                self.msg_aggregator.add_error(
                    f'Error processing Bitcoin.de {asset} balance entry due to inability to '
                    f'deserialize the amount due to {str(e)}. Skipping balance entry',
                )
                continue

            assets_balance[asset] = Balance(
                amount=amount,
                usd_value=amount * usd_price,
            )

        return assets_balance, ''
Beispiel #26
0
    def query_ethereum_tokens(
        self,
        tokens: List[EthereumToken],
        eth_balances: EthBalances,
    ) -> None:
        token_balances = {}
        token_usd_price = {}
        for token in tokens:
            usd_price = Inquirer().find_usd_price(token)
            if usd_price == 0:
                # skip tokens that have no price
                continue
            token_usd_price[token] = usd_price

            token_balances[token] = self._query_token_balances(
                token_asset=token,
                query_callback=self.ethchain.get_multitoken_balance,
                accounts=self.accounts.eth,
            )

        for token, token_accounts in token_balances.items():
            token_total = FVal(0)
            for account, balance in token_accounts.items():
                token_total += balance
                usd_value = balance * token_usd_price[token]
                eth_balances[account][token] = balance
                eth_balances[account]['usd_value'] = eth_balances[account][
                    'usd_value'] + usd_value

            self.totals[token] = {
                'amount': token_total,
                'usd_value': token_total * token_usd_price[token],
            }

        self.balances[A_ETH] = cast(
            Dict[BlockchainAddress, Dict[Union[str, Asset], FVal]],
            eth_balances,
        )
Beispiel #27
0
    def modify_btc_account(
        self,
        account: BTCAddress,
        append_or_remove: str,
        add_or_sub: Callable[[FVal, FVal], FVal],
    ) -> None:
        """Either appends or removes a BTC acccount.

        Call with 'append', operator.add to add the account
        Call with 'remove', operator.sub to remove the account
        """
        getattr(self.accounts.btc, append_or_remove)(account)
        btc_usd_price = Inquirer().find_usd_price(A_BTC)
        balance = self.query_btc_account_balance(account)
        usd_balance = balance * btc_usd_price
        if append_or_remove == 'append':
            self.balances[A_BTC][account] = {
                'amount': balance,
                'usd_value': usd_balance
            }
        elif append_or_remove == 'remove':
            del self.balances[A_BTC][account]
        else:
            raise ValueError('Programmer error: Should be append or remove')

        if len(self.balances[A_BTC]) == 0:
            # If the last account was removed balance should be 0
            self.totals[A_BTC]['amount'] = FVal(0)
            self.totals[A_BTC]['usd_value'] = FVal(0)
        else:
            self.totals[A_BTC]['amount'] = add_or_sub(
                self.totals[A_BTC].get('amount', FVal(0)),
                balance,
            )
            self.totals[A_BTC]['usd_value'] = add_or_sub(
                self.totals[A_BTC].get('usd_value', FVal(0)),
                usd_balance,
            )
Beispiel #28
0
    def get_balances(
        self,
        addresses: List[ChecksumAddress],
    ) -> Dict[ChecksumAddress, Balance]:
        """Return the addresses' balances (staked amount per pool) in the AdEx
        protocol.

        May raise:
        - RemoteError: Problem querying the chain
        """
        if len(addresses) == 0:
            return {}

        result = multicall_specific(
            ethereum=self.ethereum,
            contract=self.staking_pool,
            method_name='balanceOf',
            arguments=[[x] for x in addresses],
        )
        if all(x[0] == 0 for x in result):
            return {}  # no balances found

        staking_balances = {}
        usd_price = Inquirer().find_usd_price(A_ADX)
        share_price = self.staking_pool.call(self.ethereum, 'shareValue')
        for idx, address in enumerate(addresses):
            balance = result[idx][0]
            if balance == 0:
                continue
            # else the address has staked adex
            amount = token_normalized_value_decimals(
                token_amount=balance * share_price / (FVal(10)**18),
                token_decimals=18,
            )
            staking_balances[address] = Balance(amount=amount,
                                                usd_value=amount * usd_price)

        return staking_balances
Beispiel #29
0
def test_logout_and_login_again(rotkehlchen_server, username):
    """Test that when a rotkehlchen user logs out they can properly login again

    Tests that unlock works correctly and returns proper response

    Also regression test for https://github.com/rotkehlchenio/rotkehlchen/issues/288
    """
    rotkehlchen_server.logout()
    assert not rotkehlchen_server.rotkehlchen.user_is_logged_in
    response = rotkehlchen_server.unlock_user(
        user=username,
        password='******',
        create_new=False,
        sync_approval='unknown',
        api_key='',
        api_secret='',
    )
    check_proper_unlock_result(response)

    assert rotkehlchen_server.rotkehlchen.user_is_logged_in
    # The bug for #288 was here. The inquirer instance was None and any
    # queries utilizing it were throwing exceptions.
    Inquirer().get_fiat_usd_exchange_rates(currencies=None)
Beispiel #30
0
    def query_balances(self) -> Tuple[Optional[dict], str]:

        resp = self._api_query_dict('get', 'user/wallet', {'currency': 'XBt'})
        # Bitmex shows only BTC balance
        returned_balances = dict()
        usd_price = Inquirer().find_usd_price(A_BTC)
        # result is in satoshis
        amount = satoshis_to_btc(FVal(resp['amount']))
        usd_value = amount * usd_price

        returned_balances[A_BTC] = dict(
            amount=amount,
            usd_value=usd_value,
        )
        log.debug(
            'Bitmex balance query result',
            sensitive_log=True,
            currency='BTC',
            amount=amount,
            usd_value=usd_value,
        )

        return returned_balances, ''