def __init__( self, blockchain_accounts, all_eth_tokens, owned_eth_tokens, inquirer, ethrpc_port ): self.lock = Semaphore() self.results_cache = {} self.ethchain = Ethchain(ethrpc_port) self.inquirer = inquirer self.accounts = blockchain_accounts self.owned_eth_tokens = owned_eth_tokens # All the known tokens, along with addresses and decimals self.all_eth_tokens = {} for token in all_eth_tokens: try: token_symbol = str(token['symbol']) except (UnicodeDecodeError, UnicodeEncodeError): # skip tokens with problems in unicode encoding decoding continue self.all_eth_tokens[token_symbol] = { 'address': token['address'], 'decimal': token['decimal'] } # Per account balances self.balances = defaultdict(dict) # Per asset total balances self.totals = defaultdict(dict)
def unlock_user(self, user, password, create_new, sync_approval, api_key, api_secret): log.info( 'Unlocking user', user=user, create_new=create_new, sync_approval=sync_approval, ) # unlock or create the DB self.password = password self.user_directory = self.data.unlock(user, password, create_new) self.try_premium_at_start( api_key=api_key, api_secret=api_secret, create_new=create_new, sync_approval=sync_approval, ) 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( user_directory=self.user_directory, db=self.data.db, eth_accounts=self.data.get_eth_accounts(), historical_data_start=historical_data_start, ) self.inquirer = Inquirer(data_dir=self.data_dir, kraken=self.kraken) price_historian = PriceHistorian( data_directory=self.data_dir, history_date_start=historical_data_start, inquirer=self.inquirer, ) db_settings = self.data.db.get_settings() self.accountant = Accountant( price_historian=price_historian, profit_currency=self.data.main_currency(), user_directory=self.user_directory, 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'], include_gas_costs=db_settings['include_gas_costs'], ) # Initialize the rotkehlchen logger LoggingSettings(anonymized_logs=db_settings['anonymized_logs']) 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, )
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, )
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)
def ethchain_client(ethrpc_port): return Ethchain(ethrpc_port, attempt_connect=False)
def ethchain_client(ethrpc_port, etherscan): ethrpc_endpoint = f'http://localhost:{ethrpc_port}' return Ethchain(ethrpc_endpoint, etherscan=etherscan, attempt_connect=False)
def unlock_user( self, user: str, password: str, create_new: bool, sync_approval: bool, api_key: ApiKey, api_secret: ApiSecret, ) -> None: """Unlocks an existing user or creates a new one if `create_new` is True""" log.info( 'Unlocking user', user=user, create_new=create_new, sync_approval=sync_approval, ) # unlock or create the DB self.password = password self.user_directory = self.data.unlock(user, password, create_new) self.last_data_upload_ts = self.data.db.get_last_data_upload_ts() self.premium_sync_manager = PremiumSyncManager(data=self.data, password=password) try: self.premium = self.premium_sync_manager.try_premium_at_start( api_key=api_key, api_secret=api_secret, username=user, create_new=create_new, sync_approval=sync_approval, ) except AuthenticationError: # It means that our credentials were not accepted by the server # or some other error happened pass exchange_credentials = self.data.db.get_exchange_credentials() settings = self.data.db.get_settings() historical_data_start = settings['historical_data_start'] eth_rpc_endpoint = settings['eth_rpc_endpoint'] self.trades_historian = TradesHistorian( user_directory=self.user_directory, db=self.data.db, eth_accounts=self.data.get_eth_accounts(), msg_aggregator=self.msg_aggregator, ) # Initialize the price historian singleton PriceHistorian( data_directory=self.data_dir, history_date_start=historical_data_start, cryptocompare=Cryptocompare(data_directory=self.data_dir), ) db_settings = self.data.db.get_settings() self.accountant = Accountant( profit_currency=self.data.main_currency(), user_directory=self.user_directory, msg_aggregator=self.msg_aggregator, 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'], include_gas_costs=db_settings['include_gas_costs'], ) # Initialize the rotkehlchen logger LoggingSettings(anonymized_logs=db_settings['anonymized_logs']) self.initialize_exchanges(exchange_credentials) ethchain = Ethchain(eth_rpc_endpoint) self.blockchain = Blockchain( blockchain_accounts=self.data.db.get_blockchain_accounts(), owned_eth_tokens=self.data.db.get_owned_tokens(), ethchain=ethchain, msg_aggregator=self.msg_aggregator, ) self.user_is_logged_in = True
class Blockchain(object): def __init__( self, blockchain_accounts, all_eth_tokens, owned_eth_tokens, inquirer, ethrpc_port ): self.lock = Semaphore() self.results_cache = {} self.ethchain = Ethchain(ethrpc_port) self.inquirer = inquirer self.accounts = blockchain_accounts self.owned_eth_tokens = owned_eth_tokens # All the known tokens, along with addresses and decimals self.all_eth_tokens = {} for token in all_eth_tokens: try: token_symbol = str(token['symbol']) except (UnicodeDecodeError, UnicodeEncodeError): # skip tokens with problems in unicode encoding decoding continue self.all_eth_tokens[token_symbol] = { 'address': token['address'], 'decimal': token['decimal'] } # Per account balances self.balances = defaultdict(dict) # Per asset total balances self.totals = defaultdict(dict) def set_eth_rpc_port(self, port): return self.ethchain.set_rpc_port(port) @property def eth_tokens(self): return self.owned_eth_tokens @cache_response_timewise() def query_balances(self): self.query_ethereum_balances() self.query_btc_balances() return {'per_account': self.balances, 'totals': self.totals} def query_btc_account_balance(self, account): btc_resp = urlopen(Request( 'https://blockchain.info/q/addressbalance/%s' % account )) return FVal(btc_resp.read()) * FVal('0.00000001') # result is in satoshis def query_btc_balances(self): if 'BTC' not in self.accounts: return self.balances['BTC'] = {} btc_usd_price = self.inquirer.find_usd_price('BTC') total = FVal(0) for account in self.accounts['BTC']: balance = self.query_btc_account_balance(account) total += balance self.balances['BTC'][account] = {'amount': balance, 'usd_value': balance * btc_usd_price} self.totals['BTC'] = {'amount': total, 'usd_value': total * btc_usd_price} def track_new_tokens(self, tokens): intersection = set(tokens).intersection(set(self.owned_eth_tokens)) if intersection != set(): raise InputError('Some of the new provided tokens to track already exist') self.owned_eth_tokens.extend(tokens) self.query_ethereum_tokens(tokens, self.balances['ETH']) return {'per_account': self.balances, 'totals': self.totals} def remove_eth_tokens(self, tokens): for token in tokens: usd_price = self.inquirer.find_usd_price(token) for account, account_data in self.balances['ETH'].items(): if token not in account_data: continue balance = account_data[token] deleting_usd_value = balance * usd_price del self.balances['ETH'][account][token] self.balances['ETH'][account]['usd_value'] = ( self.balances['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} def modify_btc_account(self, account, append_or_remove, add_or_sub): """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 = self.inquirer.find_usd_price('BTC') balance = self.query_btc_account_balance(account) usd_balance = balance * btc_usd_price if append_or_remove == 'append': self.balances['BTC'][account] = {'amount': balance, 'usd_value': usd_balance} elif append_or_remove == 'remove': del self.balances['BTC'][account] else: raise ValueError('Programmer error: Should be append or remove') self.totals['BTC']['amount'] = add_or_sub(self.totals['BTC'].get('amount', 0), balance) self.totals['BTC']['usd_value'] = add_or_sub( self.totals['BTC'].get('usd_value', 0), usd_balance ) def modify_eth_account(self, account, append_or_remove, add_or_sub): """Either appends or removes an ETH acccount. Call with 'append', operator.add to add the account Call with 'remove', operator.sub to remove the account """ getattr(self.accounts['ETH'], append_or_remove)(account) eth_usd_price = self.inquirer.find_usd_price('ETH') balance = self.ethchain.get_eth_balance(account) usd_balance = balance * eth_usd_price if append_or_remove == 'append': self.balances['ETH'][account] = {'ETH': balance, 'usd_value': usd_balance} elif append_or_remove == 'remove': del self.balances['ETH'][account] else: raise ValueError('Programmer error: Should be append or remove') self.totals['ETH']['amount'] = add_or_sub(self.totals['ETH'].get('amount', 0), balance) self.totals['ETH']['usd_value'] = add_or_sub( self.totals['ETH'].get('usd_value', 0), usd_balance ) for token in self.owned_eth_tokens: usd_price = self.inquirer.find_usd_price(token) if usd_price == 0: # skip tokens that have no price continue token_balance = self.ethchain.get_token_balance( token, self.all_eth_tokens[token]['address'], self.all_eth_tokens[token]['decimal'], account ) if token_balance == 0: continue usd_value = token_balance * usd_price if append_or_remove == 'append': account_balance = self.balances['ETH'][account] account_balance[token] = balance account_balance['usd_value'] = account_balance['usd_value'] + usd_value self.totals[token] = { 'amount': add_or_sub(self.totals[token].get('amount', 0), token_balance), 'usd_value': add_or_sub(self.totals.get('usd_value', 0), usd_value) } def add_blockchain_account(self, blockchain, account): return self.modify_blockchain_account(blockchain, account, 'append', operator.add) def remove_blockchain_account(self, blockchain, account): return self.modify_blockchain_account(blockchain, account, 'remove', operator.sub) def modify_blockchain_account(self, blockchain, account, append_or_remove, add_or_sub): if blockchain == 'BTC': if append_or_remove == 'remove' and account not in self.accounts['BTC']: raise InputError('Tried to remove a non existing BTC account') self.modify_btc_account(account, append_or_remove, add_or_sub) elif blockchain == 'ETH': if append_or_remove == 'remove' and account not in self.accounts['ETH']: raise InputError('Tried to remove a non existing ETH account') self.modify_eth_account(account, append_or_remove, add_or_sub) else: raise InputError( 'Unsupported blockchain {} provided at remove_blockchain_account'.format( blockchain) ) return {'per_account': self.balances, 'totals': self.totals} def query_ethereum_tokens(self, tokens, eth_balances): token_balances = {} token_usd_price = {} for token in tokens: usd_price = self.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.ethchain.get_multitoken_balance( token, self.all_eth_tokens[token]['address'], self.all_eth_tokens[token]['decimal'], 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['ETH'] = eth_balances def query_ethereum_balances(self): if 'ETH' not in self.accounts: return eth_accounts = self.accounts['ETH'] eth_usd_price = self.inquirer.find_usd_price('ETH') balances = self.ethchain.get_multieth_balance(eth_accounts) eth_total = FVal(0) eth_balances = {} for account, balance in balances.items(): eth_total += balance eth_balances[account] = {'ETH': balance, 'usd_value': balance * eth_usd_price} self.totals['ETH'] = {'amount': eth_total, 'usd_value': eth_total * eth_usd_price} self.balances['ETH'] = eth_balances # but they are not complete until token query # And now for tokens self.query_ethereum_tokens(self.owned_eth_tokens, eth_balances)