Repository: rotki/rotki

Issue:
Saving the user's balances with certain kinds of Manual Balances fails
## Problem Definition

Saving the user's balances sometimes fails in when trying to save a Manual Balance. As a result, balances are not saved and net-worth is not updated.

In this specific case I have added a manual balance of 100 USD in Location `banks` which seems to cause the below error in the back-end during the save_balances method.

We should also investigate to what extent it is possible to send these errors to the front-end through the `Messages` API call or another way to notify the user that something has gone wrong in the backend.

Log excerpt:
```
27/04/2020 14:33:09 W. Europe Daylight Time -- ERROR:rotkehlchen.api.rest:Greenlet for task 0 dies with exception: at this point the key should be Asset type and not <class 'str'> USD.
Exception Name: <class 'AssertionError'>
Exception Info: at this point the key should be Asset type and not <class 'str'> USD
Traceback:
   File "src/gevent/greenlet.py", line 818, in gevent._greenlet.Greenlet.run
  File "c:\users\gebruiker\source\repos\rotki\rotkehlchen\api\rest.py", line 214, in _do_query_async
    result = getattr(self, command)(**kwargs)
  File "c:\users\gebruiker\source\repos\rotki\rotkehlchen\api\rest.py", line 314, in _query_all_balances
    ignore_cache=ignore_cache,
  File "c:\users\gebruiker\source\repos\rotki\rotkehlchen\rotkehlchen.py", line 564, in query_balances
    self.data.save_balances_data(data=result_dict, timestamp=timestamp)
  File "c:\users\gebruiker\source\repos\rotki\rotkehlchen\data_handler.py", line 105, in save_balances_data
    self.db.write_balances_data(data=data, timestamp=timestamp)
  File "c:\users\gebruiker\source\repos\rotki\rotkehlchen\db\dbhandler.py", line 1047, in write_balances_data
    assert isinstance(key, Asset), msg
```




Discussion:

Resolving patch:
diff --git a/rotkehlchen/balances/manual.py b/rotkehlchen/balances/manual.py
--- a/rotkehlchen/balances/manual.py
+++ b/rotkehlchen/balances/manual.py
@@ -100,20 +100,20 @@ def account_for_manually_tracked_balances(
         location_str = str(m_entry.location)
         if location_str not in balances:
             balances[location_str] = {}
-            balances[location_str][m_entry.asset.identifier] = {
+            balances[location_str][m_entry.asset] = {
                 'amount': m_entry.amount,
                 'usd_value': m_entry.usd_value,
             }
         else:
-            if m_entry.asset.identifier not in balances[location_str]:
-                balances[location_str][m_entry.asset.identifier] = {
+            if m_entry.asset not in balances[location_str]:
+                balances[location_str][m_entry.asset] = {
                     'amount': m_entry.amount,
                     'usd_value': m_entry.usd_value,
                 }
             else:
-                old_amount = balances[location_str][m_entry.asset.identifier]['amount']
-                old_usd_value = balances[location_str][m_entry.asset.identifier]['usd_value']
-                balances[location_str][m_entry.asset.identifier] = {
+                old_amount = balances[location_str][m_entry.asset]['amount']
+                old_usd_value = balances[location_str][m_entry.asset]['usd_value']
+                balances[location_str][m_entry.asset] = {
                     'amount': old_amount + m_entry.amount,
                     'usd_value': old_usd_value + m_entry.usd_value,
                 }
diff --git a/rotkehlchen/db/dbhandler.py b/rotkehlchen/db/dbhandler.py
--- a/rotkehlchen/db/dbhandler.py
+++ b/rotkehlchen/db/dbhandler.py
@@ -1043,7 +1043,8 @@ def write_balances_data(self, data: BalancesData, timestamp: Timestamp) -> None:
             if key in ('location', 'net_usd'):
                 continue
 
-            assert isinstance(key, Asset), 'at this point the key should only be Asset type'
+            msg = f'at this point the key should be of Asset type and not {type(key)} {str(key)}'
+            assert isinstance(key, Asset), msg
             balances.append(AssetBalance(
                 time=timestamp,
                 asset=key,


Test patch:
diff --git a/rotkehlchen/tests/api/test_manually_tracked_balances.py b/rotkehlchen/tests/api/test_manually_tracked_balances.py
--- a/rotkehlchen/tests/api/test_manually_tracked_balances.py
+++ b/rotkehlchen/tests/api/test_manually_tracked_balances.py
@@ -13,6 +13,7 @@
     assert_proper_response,
     assert_proper_response_with_result,
 )
+from rotkehlchen.utils.misc import ts_now
 
 
 def _populate_tags(api_server):
@@ -118,6 +119,7 @@ def test_add_and_query_manually_tracked_balances(
         rotkehlchen_api_server,
 ):
     """Test that adding and querying manually tracked balances via the API works fine"""
+    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
     _populate_tags(rotkehlchen_api_server)
     response = requests.get(
         api_url_for(
@@ -142,6 +144,23 @@ def test_add_and_query_manually_tracked_balances(
     result = assert_proper_response_with_result(response)
     assert_balances_match(expected_balances=balances, returned_balances=result['balances'])
 
+    now = ts_now()
+    # Also now test for https://github.com/rotki/rotki/issues/942 by querying for all balances
+    # causing all balances to be saved and making sure the manual balances also got saved
+    response = requests.get(
+        api_url_for(
+            rotkehlchen_api_server,
+            "allbalancesresource",
+        ),
+    )
+    result = assert_proper_response_with_result(response)
+    assert result['BTC']['amount'] == '1.425'
+    assert result['XMR']['amount'] == '50.315'
+    assert result['BNB']['amount'] == '155'
+    # Check DB to make sure a save happened
+    assert rotki.data.db.get_last_balance_save_time() >= now
+    assert set(rotki.data.db.query_owned_assets()) == {'BTC', 'XMR', 'BNB', 'ETH'}
+
 
 def test_add_manually_tracked_balances_no_price(rotkehlchen_api_server):
     """Test that adding a manually tracked balance of an asset for which we cant