def test_atoken_to_asset(): cursor = GlobalDBHandler()._conn.cursor() result = cursor.execute( 'SELECT A.address from ethereum_tokens as A LEFT OUTER JOIN assets as B ' 'WHERE A.address=B.details_reference AND A.protocol IN (?, ?)', ('aave', 'aave-v2'), ) for entry in result: atoken = EthereumToken(entry[0]) reserve_asset = atoken_to_asset(atoken) if atoken in ATOKENV1_TO_ASSET: assert reserve_asset == ATOKENV1_TO_ASSET[atoken] else: assert reserve_asset == ATOKENV2_ADDRESS_TO_RESERVE_ASSET[atoken.ethereum_address] for atokenv1, reserve_asset in ATOKENV1_TO_ASSET.items(): assert atoken_to_asset(atokenv1) == reserve_asset
def atoken_to_asset(atoken: EthereumToken) -> Optional[Asset]: if atoken == A_AETH_V1: return A_ETH if atoken == A_AREP_V1: return A_REP asset_symbol = atoken.symbol[1:] cursor = GlobalDBHandler().conn.cursor() result = cursor.execute( 'SELECT A.address from ethereum_tokens as A LEFT OUTER JOIN assets as B ' 'WHERE A.address=B.details_reference AND B.symbol=? COLLATE NOCASE', (asset_symbol,), ).fetchall() if len(result) != 1: log.error(f'Could not find asset from {atoken} since multiple or no results were returned') return None return Asset(ethaddress_to_identifier(result[0][0]))
def asset_to_atoken(asset: Asset, version: int) -> Optional[EthereumToken]: if asset == A_ETH: return A_AETH_V1 protocol = 'aave' if version == 1 else 'aave-v2' cursor = GlobalDBHandler().conn.cursor() result = cursor.execute( 'SELECT A.address from ethereum_tokens as A LEFT OUTER JOIN assets as B ' 'WHERE A.protocol==? AND A.address=B.details_reference AND B.symbol=?', (protocol, 'a' + asset.symbol), ).fetchall() if len(result) != 1: log.error(f'Could not derive atoken from {asset} since multiple or no results were returned') # noqa: E501 return None try: return EthereumToken(result[0][0]) except UnknownAsset: # should not happen log.error(f'Could not derive atoken from {asset}. Couldnt turn {result[0]} to EthereumToken') # noqa: E501 return None
def export_assets_from_file( dirpath: Optional[Path], db_handler: 'DBHandler', ) -> Path: """ Creates a zip file with a json file containing the assets added by the user. May raise: - PermissionError if the temp file can't be created """ if dirpath is None: dirpath = Path(tempfile.TemporaryDirectory().name) dirpath.mkdir(parents=True, exist_ok=True) assets = GlobalDBHandler().get_user_added_assets(user_db=db_handler) serialized = [] for asset_identifier in assets: try: asset = Asset(asset_identifier) serialized.append(asset.to_dict()) except UnknownAsset as e: log.error(e) cursor = GlobalDBHandler().conn.cursor() query = cursor.execute('SELECT value from settings WHERE name="version";') version = query.fetchone()[0] data = { 'version': version, 'assets': serialized, } zip_path = dirpath / 'assets.zip' with ZipFile(file=zip_path, mode='w', compression=ZIP_DEFLATED) as assets_zip: assets_zip.writestr( zinfo_or_arcname='assets.json', data=json.dumps(data), ) return zip_path
def test_adding_custom_tokens(rotkehlchen_api_server): """Test that the endpoint for adding a custom ethereum token works""" response = requests.put( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'token': CUSTOM_TOKEN3.serialize()}, ) result = assert_proper_response_with_result(response) assert result == {'identifier': ETHEREUM_DIRECTIVE + CUSTOM_TOKEN3.address} response = requests.get( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), ) result = assert_proper_response_with_result(response) expected_tokens = INITIAL_EXPECTED_TOKENS.copy() + [ CUSTOM_TOKEN3, CustomEthereumToken(address=underlying_address4), ] expected_result = [x.serialize() for x in expected_tokens] assert_token_entry_exists_in_result(result, expected_result) # test that adding an already existing address is handled properly response = requests.put( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'token': INITIAL_TOKENS[1].serialize()}, ) expected_msg = ( f'Ethereum token with address {INITIAL_TOKENS[1].address} already ' f'exists in the DB', ) assert_error_response( response=response, contained_in_msg=expected_msg, status_code=HTTPStatus.CONFLICT, ) # also test that the addition of underlying tokens has created proper asset entires for them cursor = GlobalDBHandler()._conn.cursor() result = cursor.execute( 'SELECT COUNT(*) from assets WHERE identifier IN (?, ?, ?, ?)', [ ETHEREUM_DIRECTIVE + x for x in [ underlying_address1, underlying_address2, underlying_address3, underlying_address4 ] ], # noqa: E501 ).fetchone()[0] assert result == 4 result = cursor.execute( 'SELECT COUNT(*) from ethereum_tokens WHERE address IN (?, ?, ?, ?)', (underlying_address1, underlying_address2, underlying_address3, underlying_address4), # noqa: E501 ).fetchone()[0] assert result == 4 # now test that adding a token with underlying tokens adding up to more than 100% is caught bad_token = CustomEthereumToken( address=make_ethereum_address(), decimals=18, name='foo', symbol='BBB', underlying_tokens=[ UnderlyingToken(address=make_ethereum_address(), weight=FVal('0.5055')), UnderlyingToken(address=make_ethereum_address(), weight=FVal('0.7055')), ], ) response = requests.put( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'token': bad_token.serialize()}, ) expected_msg = ( f'The sum of underlying token weights for {bad_token.address} is ' f'121.1000 and exceeds 100%') assert_error_response( response=response, contained_in_msg=expected_msg, status_code=HTTPStatus.BAD_REQUEST, ) # and test that adding a token with underlying tokens adding up to less than 100% is caught bad_token = CustomEthereumToken( address=make_ethereum_address(), decimals=18, name='foo', symbol='BBB', underlying_tokens=[ UnderlyingToken(address=make_ethereum_address(), weight=FVal('0.1055')), UnderlyingToken(address=make_ethereum_address(), weight=FVal('0.2055')), ], ) response = requests.put( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'token': bad_token.serialize()}, ) expected_msg = ( f'The sum of underlying token weights for {bad_token.address} is ' f'31.1000 and does not add up to 100%') assert_error_response( response=response, contained_in_msg=expected_msg, status_code=HTTPStatus.BAD_REQUEST, ) # and test that adding a token with empty list of underlying tokens and not null is an error bad_token = CustomEthereumToken( address=make_ethereum_address(), decimals=18, name='foo', symbol='BBB', underlying_tokens=[], ) serialized_bad_token = bad_token.serialize() serialized_bad_token['underlying_tokens'] = [] response = requests.put( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'token': serialized_bad_token}, ) expected_msg = ( f'Gave an empty list for underlying tokens of {bad_token.address}') assert_error_response( response=response, contained_in_msg=expected_msg, status_code=HTTPStatus.BAD_REQUEST, )
def test_deleting_custom_tokens(rotkehlchen_api_server): """Test that the endpoint for deleting a custom ethereum token works""" token0_id = ETHEREUM_DIRECTIVE + INITIAL_TOKENS[0].address token1_id = ETHEREUM_DIRECTIVE + INITIAL_TOKENS[1].address underlying1_id = ETHEREUM_DIRECTIVE + underlying_address1 underlying2_id = ETHEREUM_DIRECTIVE + underlying_address2 underlying3_id = ETHEREUM_DIRECTIVE + underlying_address3 cursor = GlobalDBHandler()._conn.cursor() # Make sure the equivalent assets we will delete exist in the DB result = cursor.execute( 'SELECT COUNT(*) from assets WHERE identifier IN (?, ?, ?, ?, ?)', (token0_id, token1_id, underlying1_id, underlying2_id, underlying3_id), ).fetchone()[0] assert result == 5 response = requests.delete( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'address': INITIAL_TOKENS[1].address}, ) result = assert_proper_response_with_result(response) assert result == {'identifier': token1_id} response = requests.get( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), ) result = assert_proper_response_with_result(response) expected_tokens = INITIAL_EXPECTED_TOKENS[:-1] expected_result = [x.serialize() for x in expected_tokens] assert_token_entry_exists_in_result(result, expected_result) # also check the mapping for the underlying still tokens exists result = cursor.execute( 'SELECT COUNT(*) from underlying_tokens_list').fetchone()[0] assert result == 3 # test that deleting a non existing address is handled properly non_existing_address = make_ethereum_address() response = requests.delete( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'address': non_existing_address}, ) expected_msg = ( f'Tried to delete ethereum token with address {non_existing_address} ' f'but it was not found in the DB', ) assert_error_response( response=response, contained_in_msg=expected_msg, status_code=HTTPStatus.CONFLICT, ) # test that trying to delete an underlying token that exists in a mapping # of another token is handled correctly non_existing_address = make_ethereum_address() response = requests.delete( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'address': underlying_address1}, ) expected_msg = ( f'Tried to delete ethereum token with address {underlying_address1} ' f'but its deletion would violate a constraint so deletion failed') assert_error_response( response=response, contained_in_msg=expected_msg, status_code=HTTPStatus.CONFLICT, ) # Check that the initial token of the test has MKR as swapped for token # this is just a sanity check as the fixture initialization should take care of it response = requests.get( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'address': INITIAL_TOKENS[0].address}, ) result = assert_proper_response_with_result(response) assert result['swapped_for'] == 'MKR' # test that trying to delete a token (MKR) that is used as swapped_for # of another token is handled correctly non_existing_address = make_ethereum_address() response = requests.delete( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'address': A_MKR.ethereum_address}, ) expected_msg = ( f'Tried to delete ethereum token with address {A_MKR.ethereum_address} ' f'but its deletion would violate a constraint so deletion failed') assert_error_response( response=response, contained_in_msg=expected_msg, status_code=HTTPStatus.CONFLICT, ) # now test that deleting the token with underlying tokens works response = requests.delete( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), json={'address': INITIAL_TOKENS[0].address}, ) result = assert_proper_response_with_result(response) assert result == {'identifier': token0_id} response = requests.get( api_url_for( rotkehlchen_api_server, 'ethereumassetsresource', ), ) result = assert_proper_response_with_result(response) expected_tokens = INITIAL_EXPECTED_TOKENS[1:-1] expected_result = [x.serialize() for x in expected_tokens] assert_token_entry_exists_in_result(result, expected_result) # and removes the mapping of all underlying tokens result = cursor.execute( 'SELECT COUNT(*) from underlying_tokens_list').fetchone()[0] assert result == 0 # and that the equivalent asset entries were also deleted result = cursor.execute( 'SELECT COUNT(*) from assets WHERE identifier IN (?, ?)', (token0_id, token1_id), ).fetchone()[0] assert result == 0