def get_eth_balance(self, account: typing.EthAddress) -> FVal: if not self.connected: log.debug( 'Querying etherscan for account balance', sensitive_log=True, eth_address=account, ) eth_resp = request_get_dict( 'https://api.etherscan.io/api?module=account&action=balance&address=%s' % account, ) if eth_resp['status'] != 1: raise ValueError( 'Failed to query etherscan for accounts balance') amount = FVal(eth_resp['result']) log.debug( 'Etherscan account balance result', sensitive_log=True, eth_address=account, wei_amount=amount, ) return from_wei(amount) else: wei_amount = self.web3.eth.getBalance(account) # pylint: disable=no-member log.debug( 'Ethereum node account balance result', sensitive_log=True, eth_address=account, wei_amount=wei_amount, ) return from_wei(wei_amount)
def get_multieth_balance( self, accounts: List[typing.EthAddress], ) -> Dict[typing.EthAddress, FVal]: """Returns a dict with keys being accounts and balances in ETH""" balances = {} if not self.connected: if len(accounts) > 20: new_accounts = [ accounts[x:x + 2] for x in range(0, len(accounts), 2) ] else: new_accounts = [accounts] for account_slice in new_accounts: log.debug( 'Querying etherscan for multiple accounts balance', sensitive_log=True, eth_accounts=account_slice, ) eth_resp = request_get_dict( 'https://api.etherscan.io/api?module=account&action=balancemulti&address=%s' % ','.join(account_slice), ) if eth_resp['status'] != 1: raise ValueError( 'Failed to query etherscan for accounts balance') eth_accounts = eth_resp['result'] for account_entry in eth_accounts: amount = FVal(account_entry['balance']) balances[account_entry['account']] = from_wei(amount) log.debug( 'Etherscan account balance result', sensitive_log=True, eth_address=account_entry['account'], wei_amount=amount, ) else: for account in accounts: amount = FVal(self.web3.eth.getBalance(account)) # pylint: disable=no-member log.debug( 'Ethereum node balance result', sensitive_log=True, eth_address=account, wei_amount=amount, ) balances[account] = from_wei(amount) return balances
def query_eth_highest_block() -> Optional[int]: """ Attempts to query blockcypher for the block height Returns the highest blockNumber""" url = 'https://api.blockcypher.com/v1/eth/main' log.debug('Querying ETH highest block', url=url) eth_resp = request_get_dict(url) if 'height' not in eth_resp: return None block_number = int(eth_resp['height']) log.debug('ETH highest block result', block=block_number) return block_number
def _query_exchanges_rateapi(base: FiatAsset, quote: FiatAsset) -> Optional[FVal]: log.debug( 'Querying api.exchangeratesapi.io fiat pair', base_currency=base, quote_currency=quote, ) querystr = f'https://api.exchangeratesapi.io/latest?base={base}&symbols={quote}' try: resp = request_get_dict(querystr) return FVal(resp['rates'][quote]) except (ValueError, RemoteError, KeyError): log.error( 'Querying api.exchangeratesapi.io for fiat pair failed', base_currency=base, quote_currency=quote, ) return None
def _query_currency_converterapi(base: FiatAsset, quote: FiatAsset) -> Optional[FVal]: log.debug( 'Query free.currencyconverterapi.com fiat pair', base_currency=base, quote_currency=quote, ) pair = f'{base}_{quote}' querystr = (f'https://free.currencyconverterapi.com/api/v6/convert?' f'q={pair}&apiKey={CURRENCYCONVERTER_API_KEY}') try: resp = request_get_dict(querystr) return FVal(resp['results'][pair]['val']) except (ValueError, RemoteError, KeyError): log.error( 'Querying free.currencyconverterapi.com fiat pair failed', base_currency=base, quote_currency=quote, ) return None
def _query_currency_converterapi(self, base: FiatAsset, quote: FiatAsset) -> Optional[FVal]: log.debug( 'Query free.currencyconverterapi.com fiat pair', base_currency=base, quote_currency=quote, ) pair = '{}_{}'.format(base, quote) querystr = 'https://free.currencyconverterapi.com/api/v5/convert?q={}'.format( pair) try: resp = request_get_dict(querystr) return FVal(resp['results'][pair]['val']) except (ValueError, RemoteError, KeyError): log.error( 'Querying free.currencyconverterapi.com fiat pair failed', base_currency=base, quote_currency=quote, ) return None
def get_multitoken_balance( self, token_symbol: typing.EthToken, token_address: typing.EthAddress, token_decimals: int, accounts: List[typing.EthAddress], ) -> Dict[typing.EthAddress, FVal]: """Return a dictionary with keys being accounts and value balances of token Balance value is normalized through the token decimals. """ balances = {} if self.connected: token_contract = self.web3.eth.contract( # pylint: disable=no-member address=token_address, abi=self.token_abi, ) for account in accounts: log.debug( 'Ethereum node query for token balance', sensitive_log=True, eth_address=account, token_address=token_address, token_symbol=token_symbol, ) token_amount = FVal( token_contract.functions.balanceOf(account).call()) if token_amount != 0: balances[account] = token_amount / (FVal(10)** FVal(token_decimals)) log.debug( 'Ethereum node result for token balance', sensitive_log=True, eth_address=account, token_address=token_address, token_symbol=token_symbol, amount=token_amount, ) else: for account in accounts: log.debug( 'Querying Etherscan for token balance', sensitive_log=True, eth_address=account, token_address=token_address, token_symbol=token_symbol, ) resp = request_get_dict( 'https://api.etherscan.io/api?module=account&action=' 'tokenbalance&contractaddress={}&address={}'.format( token_address, account, )) if resp['status'] != 1: raise ValueError( 'Failed to query etherscan for {} token balance of {}'. format( token_symbol, account, )) token_amount = FVal(resp['result']) if token_amount != 0: balances[account] = token_amount / (FVal(10)** FVal(token_decimals)) log.debug( 'Etherscan result for token balance', sensitive_log=True, eth_address=account, token_address=token_address, token_symbol=token_symbol, amount=token_amount, ) return balances
def query_ethereum_txlist( address: EthAddress, internal: bool, from_block: int = None, to_block: int = None, ) -> List[EthereumTransaction]: log.debug( 'Querying etherscan for tx list', sensitive_log=True, internal=internal, eth_address=address, from_block=from_block, to_block=to_block, ) result = list() if internal: reqstring = ('https://api.etherscan.io/api?module=account&action=' 'txlistinternal&address={}'.format(address)) else: reqstring = ('https://api.etherscan.io/api?module=account&action=' 'txlist&address={}'.format(address)) if from_block: reqstring += '&startblock={}'.format(from_block) if to_block: reqstring += '&endblock={}'.format(to_block) resp = request_get_dict(reqstring) if 'status' not in resp or convert_to_int(resp['status']) != 1: status = convert_to_int(resp['status']) if status == 0 and resp['message'] == 'No transactions found': return list() log.error( 'Querying etherscan for tx list failed', sensitive_log=True, internal=internal, eth_address=address, from_block=from_block, to_block=to_block, error=resp['message'], ) # else unknown error raise ValueError( 'Failed to query txlist from etherscan with query: {} . ' 'Response was: {}'.format(reqstring, resp), ) log.debug('Etherscan tx list query result', results_num=len(resp['result'])) for v in resp['result']: # internal tx list contains no gasprice gas_price = FVal(-1) if internal else FVal(v['gasPrice']) result.append( EthereumTransaction( timestamp=Timestamp(convert_to_int(v['timeStamp'])), block_number=convert_to_int(v['blockNumber']), hash=v['hash'], from_address=v['from'], to_address=v['to'], value=FVal(v['value']), gas=FVal(v['gas']), gas_price=gas_price, gas_used=FVal(v['gasUsed']), )) return result