Пример #1
0
def get_bitcoin_addresses_balances(
        accounts: List[BTCAddress]) -> Dict[BTCAddress, FVal]:
    """Queries blockchain.info or blockstream for the balances of accounts

    May raise:
    - RemotError if there is a problem querying blockchain.info or blockstream
    """
    source = 'blockchain.info'
    balances: Dict[BTCAddress, FVal] = {}
    try:
        if _have_bc1_accounts(accounts):
            # if 1 account is bech32 we have to query blockstream. blockchaininfo won't work
            source = 'blockstream'
            balances = {}
            for account in accounts:
                url = f'https://blockstream.info/api/address/{account}'
                response_data = request_get_dict(url=url,
                                                 handle_429=True,
                                                 backoff_in_seconds=4)
                stats = response_data['chain_stats']
                balance = int(stats['funded_txo_sum']) - int(
                    stats['spent_txo_sum'])
                balances[account] = satoshis_to_btc(balance)
        else:
            # split the list of accounts into sublists of 80 addresses per list to overcome:
            # https://github.com/rotki/rotki/issues/3037
            accounts_chunks = [
                accounts[x:x + 80] for x in range(0, len(accounts), 80)
            ]
            for accounts_chunk in accounts_chunks:
                params = '|'.join(accounts_chunk)
                btc_resp = request_get_dict(
                    url=f'https://blockchain.info/multiaddr?active={params}',
                    handle_429=True,
                    # If we get a 429 then their docs suggest 10 seconds
                    # https://blockchain.info/q
                    backoff_in_seconds=10,
                )
                for entry in btc_resp['addresses']:
                    balances[entry['address']] = satoshis_to_btc(
                        FVal(entry['final_balance']))
    except (
            requests.exceptions.RequestException,
            UnableToDecryptRemoteData,
            requests.exceptions.Timeout,
    ) as e:
        raise RemoteError(
            f'bitcoin external API request for balances failed due to {str(e)}'
        ) from e  # noqa: E501
    except KeyError as e:
        raise RemoteError(
            f'Malformed response when querying bitcoin blockchain via {source}.'
            f'Did not find key {e}', ) from e

    return balances
Пример #2
0
    def query_eth_highest_block(self) -> BlockNumber:
        """ Attempts to query an external service for the block height

        Returns the highest blockNumber

        May Raise RemoteError if querying fails
        """

        url = 'https://api.blockcypher.com/v1/eth/main'
        log.debug('Querying blockcypher for ETH highest block', url=url)
        eth_resp: Optional[Dict[str, str]]
        try:
            eth_resp = request_get_dict(url)
        except (RemoteError, UnableToDecryptRemoteData,
                requests.exceptions.RequestException):
            eth_resp = None

        block_number: Optional[int]
        if eth_resp and 'height' in eth_resp:
            block_number = int(eth_resp['height'])
            log.debug('ETH highest block result', block=block_number)
        else:
            block_number = self.etherscan.get_latest_block_number()
            log.debug('ETH highest block result', block=block_number)

        return BlockNumber(block_number)
Пример #3
0
def _query_exchanges_rateapi(base: Asset, quote: Asset) -> Optional[Price]:
    assert base.is_fiat(), 'fiat currency should have been provided'
    assert quote.is_fiat(), 'fiat currency should have been provided'
    log.debug(
        'Querying api.exchangeratesapi.io fiat pair',
        base_currency=base.identifier,
        quote_currency=quote.identifier,
    )
    querystr = (
        f'https://api.exchangeratesapi.io/latest?base={base.identifier}&symbols={quote.identifier}'
    )
    try:
        resp = request_get_dict(querystr)
        return Price(FVal(resp['rates'][quote.identifier]))
    except (
            RemoteError,
            KeyError,
            requests.exceptions.TooManyRedirects,
            UnableToDecryptRemoteData,
    ):
        log.error(
            'Querying api.exchangeratesapi.io for fiat pair failed',
            base_currency=base.identifier,
            quote_currency=quote.identifier,
        )
        return None
Пример #4
0
def _check_blockstream_for_transactions(
        accounts: List[BTCAddress],
) -> Dict[BTCAddress, Tuple[bool, FVal]]:
    """May raise connection errors or KeyError"""
    have_transactions = {}
    for account in accounts:
        url = f'https://blockstream.info/api/address/{account}'
        response_data = request_get_dict(url=url, handle_429=True, backoff_in_seconds=4)
        stats = response_data['chain_stats']
        balance = satoshis_to_btc(int(stats['funded_txo_sum']) - int(stats['spent_txo_sum']))
        have_txs = stats['tx_count'] != 0
        have_transactions[account] = (have_txs, balance)

    return have_transactions
Пример #5
0
def get_token_contract_creation_time(token_address: str) -> Timestamp:
    resp = request_get_dict(
        f'http://api.etherscan.io/api?module=account&action=txlist&address='
        f'{token_address}&startblock=0&endblock=999999999&sort=asc', )
    if resp['status'] != 1:
        raise ValueError(
            'Failed to query etherscan for token {token_address} creation')
    tx_list = resp['result']
    if len(tx_list) == 0:
        raise ValueError(
            'Etherscan query of {token_address} transactions returned empty list'
        )

    return Timestamp(tx_list[0]['timeStamp'].to_int(exact=True))
Пример #6
0
def _check_blockchaininfo_for_transactions(
    accounts: List[BTCAddress], ) -> Dict[BTCAddress, Tuple[bool, FVal]]:
    """May raise RemotError or KeyError"""
    have_transactions = {}
    params = '|'.join(accounts)
    btc_resp = request_get_dict(
        url=f'https://blockchain.info/multiaddr?active={params}',
        handle_429=True,
        # If we get a 429 then their docs suggest 10 seconds
        # https://blockchain.infoq/
        backoff_in_seconds=15,
    )
    for entry in btc_resp['addresses']:
        balance = satoshis_to_btc(entry['final_balance'])
        have_transactions[entry['address']] = (entry['n_tx'] != 0, balance)

    return have_transactions
Пример #7
0
def _query_currency_converterapi(base: Asset, quote: Asset) -> Optional[Price]:
    assert base.is_fiat(), 'fiat currency should have been provided'
    assert quote.is_fiat(), 'fiat currency should have been provided'
    log.debug(
        'Query free.currencyconverterapi.com fiat pair',
        base_currency=base.identifier,
        quote_currency=quote.identifier,
    )
    pair = f'{base.identifier}_{quote.identifier}'
    querystr = (f'https://free.currconv.com/api/v7/convert?'
                f'q={pair}&compact=ultra&apiKey={CURRENCYCONVERTER_API_KEY}')
    try:
        resp = request_get_dict(querystr)
        return Price(FVal(resp[pair]))
    except (ValueError, RemoteError, KeyError, UnableToDecryptRemoteData):
        log.error(
            'Querying free.currencyconverterapi.com fiat pair failed',
            base_currency=base.identifier,
            quote_currency=quote.identifier,
        )
        return None