def get_all_eth_token_info() -> List[EthTokenInfo]: if AssetResolver().eth_token_info is not None: return AssetResolver().eth_token_info # type: ignore all_tokens = [] for identifier, asset_data in AssetResolver().assets.items(): # If an unknown asset is found (can happen if list is updated but code is not) # then default to the "own chain" type" asset_type = asset_type_mapping.get(asset_data['type'], AssetType.OWN_CHAIN) if asset_type not in (AssetType.ETH_TOKEN_AND_MORE, AssetType.ETH_TOKEN): continue all_tokens.append( EthTokenInfo( identifier=identifier, address=ChecksumEthAddress(asset_data['ethereum_address']), symbol=asset_data['symbol'], name=asset_data['name'], decimals=int(asset_data['ethereum_token_decimals']), )) AssetResolver().eth_token_info = all_tokens return all_tokens
def hex_or_bytes_to_address(value: Union[bytes, str]) -> ChecksumEthAddress: """Turns a 32bit bytes/HexBytes or a hexstring into an address May raise: - ConversionError if it can't convert a value to an int or if an unexpected type is given. """ hexstr = hex_or_bytes_to_str(value) return ChecksumEthAddress(to_checksum_address('0x' + hexstr[26:]))
def deserialize_ethereum_address(symbol: str) -> ChecksumEthAddress: """This is identical to string_to_ethereum_address() TODO: But it's wrong. We should differentiate between those two functions. That one should only be used for typing purposes while this one here should be used to properly deserialize and check that symbol is indeed an ethereum address and is always checksummed. So also external input sanitization. https://github.com/rotki/rotki/issues/2334 """ return ChecksumEthAddress(HexAddress(HexStr(symbol)))
def hex_or_bytes_to_address(value: Union[bytes, str]) -> ChecksumEthAddress: """Turns a 32bit bytes/HexBytes or a hexstring into an address May raise: - DeserializationError if it can't convert a value to an int or if an unexpected type is given. """ try: hexstr = hex_or_bytes_to_str(value) except ConversionError as e: raise DeserializationError( f'Could not turn {value!r} to an ethereum address') from e return ChecksumEthAddress(to_checksum_address('0x' + hexstr[26:]))
def get_all_eth_tokens() -> List[EthTokenInfo]: all_tokens = list() for _, asset_data in AssetResolver().assets.items(): asset_type = asset_type_mapping[asset_data['type']] if asset_type not in (AssetType.ETH_TOKEN_AND_MORE, AssetType.ETH_TOKEN): continue all_tokens.append(EthTokenInfo( address=ChecksumEthAddress(asset_data['ethereum_address']), symbol=asset_data['symbol'], name=asset_data['name'], decimal=int(asset_data['ethereum_token_decimals']), )) return all_tokens
def get_all_eth_token_info() -> List[EthTokenInfo]: if AssetResolver().eth_token_info is not None: return AssetResolver().eth_token_info # type: ignore all_tokens = [] for identifier, asset_data in AssetResolver().assets.items(): asset_type = asset_type_mapping[asset_data['type']] if asset_type not in (AssetType.ETH_TOKEN_AND_MORE, AssetType.ETH_TOKEN): continue all_tokens.append(EthTokenInfo( identifier=identifier, address=ChecksumEthAddress(asset_data['ethereum_address']), symbol=asset_data['symbol'], name=asset_data['name'], decimals=int(asset_data['ethereum_token_decimals']), )) AssetResolver().eth_token_info = all_tokens return all_tokens
def string_to_ethereum_address(value: str) -> ChecksumEthAddress: """This is a conversion without any checks of a string to ethereum address Is only used for typing. """ return ChecksumEthAddress(HexAddress(HexStr(value)))
def deserialize_ethereum_address(symbol: str) -> ChecksumEthAddress: return ChecksumEthAddress(HexAddress(HexStr(symbol)))
def test_update_conflicts(rotkehlchen_api_server, globaldb): """Test that conflicts in an asset update are handled properly""" async_query = random.choice([False, True]) rotki = rotkehlchen_api_server.rest_api.rotkehlchen update_1 = """INSERT INTO assets(identifier,type,name,symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("121-ada-FADS-as", "F","A name","SYMBOL",NULL, NULL,"", "", "121-ada-FADS-as");INSERT INTO common_asset_details(asset_id, forked) VALUES("121-ada-FADS-as", "BTC"); * INSERT INTO ethereum_tokens(address, decimals, protocol) VALUES("0x6B175474E89094C44Da98b954EedeAC495271d0F", 8, "maker");INSERT INTO assets(identifier,type, name, symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("_ceth_0x6B175474E89094C44Da98b954EedeAC495271d0F", "C", "New Multi Collateral DAI", "NDAI", 1573672677, NULL, "dai", NULL, "0x6B175474E89094C44Da98b954EedeAC495271d0F"); * INSERT INTO assets(identifier,type,name,symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("DASH", "B","Dash","DASH",1337, NULL, "dash-coingecko", NULL, "DASH");INSERT INTO common_asset_details(asset_id, forked) VALUES("DASH", "BTC"); * INSERT INTO ethereum_tokens(address, decimals, protocol) VALUES("0x1B175474E89094C44Da98b954EedeAC495271d0F", 18, NULL); INSERT INTO assets(identifier,type, name, symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("_ceth_0x1B175474E89094C44Da98b954EedeAC495271d0F", "C", "Conflicting token", "CTK", 1573672677, NULL, "ctk", NULL, "0x1B175474E89094C44Da98b954EedeAC495271d0F"); * """ # noqa: E501 globaldb.add_asset( # add a conflicting token asset_id='_ceth_0x1B175474E89094C44Da98b954EedeAC495271d0F', asset_type=AssetType.ETHEREUM_TOKEN, data=EthereumToken.initialize( address=ChecksumEthAddress('0x1B175474E89094C44Da98b954EedeAC495271d0F'), decimals=12, name='Conflicting token', symbol='CTK', started=None, swapped_for=None, coingecko='ctk', cryptocompare=None, protocol=None, underlying_tokens=None, ), ) globaldb.add_user_owned_assets([Asset('_ceth_0x1B175474E89094C44Da98b954EedeAC495271d0F')]) update_patch = mock_asset_updates( original_requests_get=requests.get, latest=999999991, updates={"999999991": { "changes": 3, "min_schema_version": GLOBAL_DB_VERSION, "max_schema_version": GLOBAL_DB_VERSION, }}, sql_actions={"999999991": update_1}, ) globaldb.add_setting_value(ASSETS_VERSION_KEY, 999999990) start_assets_num = len(globaldb.get_all_asset_data(mapping=False)) with update_patch: response = requests.get( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={'async_query': async_query}, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) result = outcome['result'] assert outcome['message'] == '' else: result = assert_proper_response_with_result(response) assert result['local'] == 999999990 assert result['remote'] == 999999991 assert result['new_changes'] == 3 response = requests.post( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={'async_query': async_query}, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) assert outcome['message'] == 'Found conflicts during assets upgrade' result = outcome['result'] else: result = assert_proper_response_with_result( response, message='Found conflicts during assets upgrade', status_code=HTTPStatus.CONFLICT, ) # Make sure that nothing was committed assert globaldb.get_setting_value(ASSETS_VERSION_KEY, None) == 999999990 assert len(globaldb.get_all_asset_data(mapping=False)) == start_assets_num with pytest.raises(UnknownAsset): Asset('121-ada-FADS-as') errors = rotki.msg_aggregator.consume_errors() warnings = rotki.msg_aggregator.consume_warnings() assert len(errors) == 0, f'Found errors: {errors}' assert len(warnings) == 0, f'Found warnings: {warnings}' # See that we get 3 conflicts expected_result = [{ 'identifier': '_ceth_0x6B175474E89094C44Da98b954EedeAC495271d0F', 'local': { 'name': 'Multi Collateral Dai', 'symbol': 'DAI', 'asset_type': 'ethereum token', 'started': 1573672677, 'forked': None, 'swapped_for': None, 'ethereum_address': '0x6B175474E89094C44Da98b954EedeAC495271d0F', 'decimals': 18, 'cryptocompare': None, 'coingecko': 'dai', 'protocol': None, }, 'remote': { 'name': 'New Multi Collateral DAI', 'symbol': 'NDAI', 'asset_type': 'ethereum token', 'started': 1573672677, 'forked': None, 'swapped_for': None, 'ethereum_address': '0x6B175474E89094C44Da98b954EedeAC495271d0F', 'decimals': 8, 'cryptocompare': None, 'coingecko': 'dai', 'protocol': 'maker', }, }, { 'identifier': 'DASH', 'local': { 'name': 'Dash', 'symbol': 'DASH', 'asset_type': 'own chain', 'started': 1390095618, 'forked': None, 'swapped_for': None, 'ethereum_address': None, 'decimals': None, 'cryptocompare': None, 'coingecko': 'dash', 'protocol': None, }, 'remote': { 'name': 'Dash', 'symbol': 'DASH', 'asset_type': 'own chain', 'started': 1337, 'forked': 'BTC', 'swapped_for': None, 'ethereum_address': None, 'decimals': None, 'cryptocompare': None, 'coingecko': 'dash-coingecko', 'protocol': None, }, }, { 'identifier': '_ceth_0x1B175474E89094C44Da98b954EedeAC495271d0F', 'local': { 'asset_type': 'ethereum token', 'coingecko': 'ctk', 'cryptocompare': None, 'decimals': 12, 'ethereum_address': '0x1B175474E89094C44Da98b954EedeAC495271d0F', 'forked': None, 'name': 'Conflicting token', 'protocol': None, 'started': None, 'swapped_for': None, 'symbol': 'CTK', }, 'remote': { 'asset_type': 'ethereum token', 'coingecko': 'ctk', 'cryptocompare': None, 'decimals': 18, 'ethereum_address': '0x1b175474E89094C44DA98B954EeDEAC495271d0f', 'forked': None, 'name': 'Conflicting token', 'protocol': None, 'started': 1573672677, 'swapped_for': None, 'symbol': 'CTK', }, }] assert result == expected_result # now try the update again but specify the conflicts resolution conflicts = {'_ceth_0x6B175474E89094C44Da98b954EedeAC495271d0F': 'remote', 'DASH': 'local', '_ceth_0x1B175474E89094C44Da98b954EedeAC495271d0F': 'remote'} # noqa: E501 response = requests.post( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={'async_query': async_query, 'conflicts': conflicts}, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) assert outcome['message'] == '' result = outcome['result'] else: result = assert_proper_response_with_result( response, message='', status_code=HTTPStatus.OK, ) cursor = globaldb._conn.cursor() # check conflicts were solved as per the given choices and new asset also added assert result is True assert globaldb.get_setting_value(ASSETS_VERSION_KEY, None) == 999999991 errors = rotki.msg_aggregator.consume_errors() warnings = rotki.msg_aggregator.consume_warnings() assert len(errors) == 0, f'Found errors: {errors}' assert len(warnings) == 0, f'Found warnings: {warnings}' dai = EthereumToken('0x6B175474E89094C44Da98b954EedeAC495271d0F') assert dai.identifier == strethaddress_to_identifier('0x6B175474E89094C44Da98b954EedeAC495271d0F') # noqa: E501 assert dai.name == 'New Multi Collateral DAI' assert dai.symbol == 'NDAI' assert dai.asset_type == AssetType.ETHEREUM_TOKEN assert dai.started == 1573672677 assert dai.forked is None assert dai.swapped_for is None assert dai.coingecko == 'dai' assert dai.cryptocompare is None assert dai.ethereum_address == '0x6B175474E89094C44Da98b954EedeAC495271d0F' assert dai.decimals == 8 assert dai.protocol == 'maker' # make sure data is in both tables assert cursor.execute('SELECT COUNT(*) from ethereum_tokens WHERE address="0x6B175474E89094C44Da98b954EedeAC495271d0F";').fetchone()[0] == 1 # noqa: E501 assert cursor.execute('SELECT COUNT(*) from assets WHERE identifier="_ceth_0x6B175474E89094C44Da98b954EedeAC495271d0F";').fetchone()[0] == 1 # noqa: E501 dash = Asset('DASH') assert dash.identifier == 'DASH' assert dash.name == 'Dash' assert dash.symbol == 'DASH' assert dash.asset_type == AssetType.OWN_CHAIN assert dash.started == 1390095618 assert dash.forked is None assert dash.swapped_for is None assert dash.coingecko == 'dash' assert dash.cryptocompare is None assert cursor.execute('SELECT COUNT(*) from common_asset_details WHERE asset_id="DASH";').fetchone()[0] == 1 # noqa: E501 assert cursor.execute('SELECT COUNT(*) from assets WHERE identifier="DASH";').fetchone()[0] == 1 # noqa: E501 new_asset = Asset('121-ada-FADS-as') assert new_asset.identifier == '121-ada-FADS-as' assert new_asset.name == 'A name' assert new_asset.symbol == 'SYMBOL' assert new_asset.asset_type == AssetType.COUNTERPARTY_TOKEN assert new_asset.started is None assert new_asset.forked == 'BTC' assert new_asset.swapped_for is None assert new_asset.coingecko == '' assert new_asset.cryptocompare == '' assert cursor.execute('SELECT COUNT(*) from common_asset_details WHERE asset_id="121-ada-FADS-as";').fetchone()[0] == 1 # noqa: E501 assert cursor.execute('SELECT COUNT(*) from assets WHERE identifier="121-ada-FADS-as";').fetchone()[0] == 1 # noqa: E501 ctk = EthereumToken('0x1B175474E89094C44Da98b954EedeAC495271d0F') assert ctk.name == 'Conflicting token' assert ctk.symbol == 'CTK' assert ctk.asset_type == AssetType.ETHEREUM_TOKEN assert ctk.started == 1573672677 assert ctk.forked is None assert ctk.swapped_for is None assert ctk.coingecko == 'ctk' assert ctk.cryptocompare is None assert ctk.ethereum_address == '0x1B175474E89094C44Da98b954EedeAC495271d0F' assert ctk.decimals == 18 assert ctk.protocol is None assert cursor.execute('SELECT COUNT(*) from ethereum_tokens WHERE address="0x1B175474E89094C44Da98b954EedeAC495271d0F";').fetchone()[0] == 1 # noqa: E501 assert cursor.execute('SELECT COUNT(*) from assets WHERE identifier="_ceth_0x1B175474E89094C44Da98b954EedeAC495271d0F";').fetchone()[0] == 1 # noqa: E501
from dataclasses import asdict from eth_typing import HexAddress, HexStr from rotkehlchen.assets.asset import EthereumToken from rotkehlchen.assets.unknown_asset import UnknownEthereumToken from rotkehlchen.typing import ChecksumEthAddress SHUF_ETHEREUM_ADDRESS = ChecksumEthAddress( HexAddress(HexStr('0x3A9FfF453d50D4Ac52A6890647b823379ba36B9E')), ) SHUF_SYMBOL = 'SHUF' SHUF_NAME = 'Shuffle.Monster V3' SHUF_DECIMALS = 18 # Test initialization def test_init_default(): ue_token = UnknownEthereumToken( ethereum_address=SHUF_ETHEREUM_ADDRESS, symbol=SHUF_SYMBOL, ) assert ue_token.ethereum_address == SHUF_ETHEREUM_ADDRESS assert ue_token.symbol == SHUF_SYMBOL assert ue_token.name is None assert ue_token.decimals is None # Test operators def test_eq(): ue_token_1 = UnknownEthereumToken( ethereum_address=SHUF_ETHEREUM_ADDRESS,
def test_associated_locations(database): """Test that locations imported in different places are correctly stored in database""" # Add trades from different locations trades = [Trade( timestamp=Timestamp(1595833195), location=Location.CRYPTOCOM, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('1.0')), rate=Price(FVal('281.14')), fee=Fee(ZERO), fee_currency=A_USD, link='', notes='', ), Trade( timestamp=Timestamp(1587825824), location=Location.CRYPTOCOM, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('50.0')), rate=Price(FVal('3.521')), fee=Fee(ZERO), fee_currency=A_USD, link='', notes='', ), Trade( timestamp=Timestamp(1596014214), location=Location.BLOCKFI, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('50.0')), rate=Price(FVal('3.521')), fee=Fee(ZERO), fee_currency=A_USD, link='', notes='', ), Trade( timestamp=Timestamp(1565888464), location=Location.NEXO, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('50.0')), rate=Price(FVal('3.521')), fee=Fee(ZERO), fee_currency=A_USD, link='', notes='', ), Trade( timestamp=Timestamp(1596014214), location=Location.NEXO, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('50.0')), rate=Price(FVal('3.521')), fee=Fee(ZERO), fee_currency=A_USD, link='', notes='', ), Trade( timestamp=Timestamp(1612051199), location=Location.BLOCKFI, base_asset=symbol_to_asset_or_token('USDC'), quote_asset=symbol_to_asset_or_token('LTC'), trade_type=TradeType.BUY, amount=AssetAmount(FVal('6404.6')), rate=Price(FVal('151.6283999982779809352223797')), fee=None, fee_currency=None, link='', notes='One Time', ), Trade( timestamp=Timestamp(1595833195), location=Location.POLONIEX, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('1.0')), rate=Price(FVal('281.14')), fee=Fee(ZERO), fee_currency=A_USD, link='', notes='', ), Trade( timestamp=Timestamp(1596429934), location=Location.COINBASE, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('0.00061475')), rate=Price(FVal('309.0687271248474989833265555')), fee=Fee(ZERO), fee_currency=A_USD, link='', notes='', ), Trade( timestamp=Timestamp(1596429934), location=Location.EXTERNAL, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('1')), rate=Price(FVal('320')), fee=Fee(ZERO), fee_currency=A_USD, link='', notes='', )] # Add multiple entries for same exchange + connected exchange database.add_trades(trades) kraken_api_key1 = ApiKey('kraken_api_key') kraken_api_secret1 = ApiSecret(b'kraken_api_secret') kraken_api_key2 = ApiKey('kraken_api_key2') kraken_api_secret2 = ApiSecret(b'kraken_api_secret2') binance_api_key = ApiKey('binance_api_key') binance_api_secret = ApiSecret(b'binance_api_secret') # add mock kraken and binance database.add_exchange('kraken1', Location.KRAKEN, kraken_api_key1, kraken_api_secret1) database.add_exchange('kraken2', Location.KRAKEN, kraken_api_key2, kraken_api_secret2) database.add_exchange('binance', Location.BINANCE, binance_api_key, binance_api_secret) # Add uniswap and sushiswap events database.add_amm_events([ LiquidityPoolEvent( tx_hash='0x47ea26957ce09e84a51b51dfdab6a4ac1c3672a372eef77b15ef7677174ac847', log_index=23, address=ChecksumEthAddress('0x3163Bb273E8D9960Ce003fD542bF26b4C529f515'), timestamp=Timestamp(1590011534), event_type=EventType.MINT_SUSHISWAP, pool_address=ChecksumEthAddress('0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974'), token0=EthereumToken('0x514910771AF9Ca656af840dff83E8264EcF986CA'), token1=EthereumToken('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'), amount0=FVal('3.313676003468974932'), amount1=FVal('0.064189269269768657'), usd_price=FVal('26.94433946158740371839009166230438'), lp_amount=FVal('0.460858304063739927'), ), ]) database.add_amm_swaps([ AMMSwap( tx_hash='0xa54bf4c68d435e3c8f432fd7e62b7f8aca497a831a3d3fca305a954484ddd7b2', log_index=208, address=ChecksumEthAddress('0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974'), from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'), to_address=string_to_ethereum_address('0xC9cB53B48A2f3A9e75982685644c1870F1405CCb'), timestamp=Timestamp(1609301469), location=Location.UNISWAP, token0=EthereumToken('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'), token1=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'), amount0_in=AssetAmount(FVal('2.6455727132446468')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('1936.810111')), ), ]) database.add_balancer_events([ BalancerEvent( tx_hash='0xa54bf4c68d435e3c8f432fd7e62b7f8aca497a831a3d3fca305a954484ddd7b3', log_index=23, address=ChecksumEthAddress('0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974'), timestamp=Timestamp(1609301469), event_type=BalancerBPTEventType.MINT, pool_address_token=EthereumToken('0x514910771AF9Ca656af840dff83E8264EcF986CA'), lp_balance=Balance(amount=FVal(2), usd_value=FVal(3)), amounts=[ AssetAmount(FVal(1)), AssetAmount(FVal(2)), ], ), ]) expected_locations = { Location.KRAKEN, Location.BINANCE, Location.BLOCKFI, Location.NEXO, Location.CRYPTOCOM, Location.POLONIEX, Location.COINBASE, Location.EXTERNAL, Location.SUSHISWAP, Location.UNISWAP, Location.BALANCER, } assert set(database.get_associated_locations()) == expected_locations