Repository: rotki/rotki

Issue:
Problems with blockchain account and balances
## Problem Definition

1. Remove BTC account, works but is not reflected in the UI
2. Remove ETH account works but is not reflected in the UI and the loading bar is stuck
In console we get this:
```
vue.runtime.esm.js:619 [Vue warn]: Error in v-on handler (Promise/async): "Error: timeout of 30000ms exceeded"

found in

---> <ConfirmDialog> at src/components/dialogs/ConfirmDialog.vue
       <AccountBalances> at src/components/settings/AccountBalances.vue
         <VCard>
           <BlockchainBalances> at src/components/settings/BlockchainBalances.vue
             <AccountsBalances> at src/views/AccountsBalances.vue
               <VContent>
                 <VApp>
                   <App> at src/App.vue
                     <Root>
warn @ vue.runtime.esm.js:619
logError @ vue.runtime.esm.js:1884
globalHandleError @ vue.runtime.esm.js:1879
handleError @ vue.runtime.esm.js:1839
(anonymous) @ vue.runtime.esm.js:1856
Promise.catch (async)
invokeWithErrorHandling @ vue.runtime.esm.js:1856
invoker @ vue.runtime.esm.js:2179
invokeWithErrorHandling @ vue.runtime.esm.js:1854
Vue.$emit @ vue.runtime.esm.js:3888
emit @ vue-property-decorator.js:223
emitter @ vue-property-decorator.js:232
click @ ConfirmDialog.vue?9154:65
invokeWithErrorHandling @ vue.runtime.esm.js:1854
invoker @ vue.runtime.esm.js:2179
invokeWithErrorHandling @ vue.runtime.esm.js:1854
Vue.$emit @ vue.runtime.esm.js:3888
click @ VBtn.js:125
invokeWithErrorHandling @ vue.runtime.esm.js:1854
invoker @ vue.runtime.esm.js:2179
original._wrapper @ vue.runtime.esm.js:6917
vue.runtime.esm.js:1888 Error: timeout of 30000ms exceeded
    at createError (createError.js:16)
    at XMLHttpRequest.handleTimeout (xhr.js:89)
```

3. Adding Eth account:  `Error while adding account: Error: timeout of 30000ms exceeded
vue.runtime.esm.js:619 [Vue warn]: Error in v-on handler (Promise/async): "Error: timeout of 30000ms exceeded"`

4. After that and with the loading screen stuck removing an ETH token does not work


## Task

Solve them and perhaps write tests (if tests take time push them after the release as I can see various things needing fixing)


Discussion:

Resolving patch:
diff --git a/rotkehlchen/blockchain.py b/rotkehlchen/blockchain.py
--- a/rotkehlchen/blockchain.py
+++ b/rotkehlchen/blockchain.py
@@ -311,7 +311,7 @@ def modify_btc_account(
         if append_or_remove == 'append':
             self.balances[A_BTC][account] = {'amount': balance, 'usd_value': usd_balance}
         elif append_or_remove == 'remove':
-            if account in self.balances[A_BTC][account]:
+            if account in self.balances[A_BTC]:
                 del self.balances[A_BTC][account]
         else:
             raise AssertionError('Programmer error: Should be append or remove')
@@ -622,8 +622,11 @@ def query_ethereum_tokens(
             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
+                if balance != ZERO:
+                    eth_balances[account][token] = balance
+                    eth_balances[account]['usd_value'] = (
+                        eth_balances[account]['usd_value'] + usd_value
+                    )
 
             self.totals[token] = {
                 'amount': token_total,


Test patch:
diff --git a/rotkehlchen/tests/api/test_blockchain.py b/rotkehlchen/tests/api/test_blockchain.py
--- a/rotkehlchen/tests/api/test_blockchain.py
+++ b/rotkehlchen/tests/api/test_blockchain.py
@@ -715,17 +715,19 @@ def test_remove_blockchain_accounts(
     rotki = rotkehlchen_api_server.rest_api.rotkehlchen
     rotki.blockchain.cache_ttl_secs = 0
 
+    removed_eth_accounts = [ethereum_accounts[0], ethereum_accounts[2]]
+    eth_accounts_after_removal = [ethereum_accounts[1], ethereum_accounts[3]]
+    all_eth_balances = ['1000000', '2000000', '3000000', '4000000']
+    token_balances = {'RDN': ['0', '0', '450000000', '0']}
+    eth_balances_after_removal = ['2000000', '4000000']
+    token_balances_after_removal = {'RDN': ['0', '0']}
     if query_balances_before_first_modification:
         # Also test by having balances queried before removing an account
-        removed_eth_accounts = [ethereum_accounts[0], ethereum_accounts[2]]
-        all_eth_accounts = [ethereum_accounts[1], ethereum_accounts[3]]
-        eth_balances = ['3000000', '4000000']
-        token_balances = {'RDN': ['0', '250000000']}
         setup = setup_balances(
             rotki,
-            ethereum_accounts=all_eth_accounts,
+            ethereum_accounts=ethereum_accounts,
             btc_accounts=btc_accounts,
-            eth_balances=eth_balances,
+            eth_balances=all_eth_balances,
             token_balances=token_balances,
         )
         with setup.etherscan_patch, setup.bitcoin_patch:
@@ -733,17 +735,11 @@ def test_remove_blockchain_accounts(
                 rotkehlchen_api_server,
                 "blockchainbalancesresource",
             ))
-
-    # Accounts to remove. Assure they are not in order to test that it does not affect anything
-    removed_eth_accounts = [ethereum_accounts[0], ethereum_accounts[2]]
-    all_eth_accounts = [ethereum_accounts[1], ethereum_accounts[3]]
-    eth_balances = ['3000000', '4000000']
-    token_balances = {'RDN': ['0', '250000000']}
     setup = setup_balances(
         rotki,
-        ethereum_accounts=all_eth_accounts,
+        ethereum_accounts=ethereum_accounts,
         btc_accounts=btc_accounts,
-        eth_balances=eth_balances,
+        eth_balances=all_eth_balances,
         token_balances=token_balances,
     )
 
@@ -761,15 +757,15 @@ def test_remove_blockchain_accounts(
     assert_eth_balances_result(
         rotki=rotki,
         json_data=json_data,
-        eth_accounts=all_eth_accounts,
-        eth_balances=setup.eth_balances,
-        token_balances=setup.token_balances,
-        also_btc=False,  # All blockchain assets have not been queried yet
+        eth_accounts=eth_accounts_after_removal,
+        eth_balances=eth_balances_after_removal,
+        token_balances=token_balances_after_removal,
+        also_btc=query_balances_before_first_modification,
     )
     # Also make sure they are removed from the DB
     accounts = rotki.data.db.get_blockchain_accounts()
     assert len(accounts.eth) == 2
-    assert all(acc in accounts.eth for acc in all_eth_accounts)
+    assert all(acc in accounts.eth for acc in eth_accounts_after_removal)
     assert len(accounts.btc) == 2
     assert all(acc in accounts.btc for acc in btc_accounts)
 
@@ -785,21 +781,22 @@ def test_remove_blockchain_accounts(
     assert_eth_balances_result(
         rotki=rotki,
         json_data=json_data,
-        eth_accounts=all_eth_accounts,
-        eth_balances=setup.eth_balances,
-        token_balances=setup.token_balances,
+        eth_accounts=eth_accounts_after_removal,
+        eth_balances=eth_balances_after_removal,
+        token_balances=token_balances_after_removal,
         also_btc=True,
     )
 
     # Now we will try to remove a BTC account. Setup the mocking infrastructure again
-    all_btc_accounts = [UNIT_BTC_ADDRESS2]
+    all_btc_accounts = [UNIT_BTC_ADDRESS1, UNIT_BTC_ADDRESS2]
+    btc_accounts_after_removal = [UNIT_BTC_ADDRESS2]
     setup = setup_balances(
         rotki,
-        ethereum_accounts=all_eth_accounts,
+        ethereum_accounts=eth_accounts_after_removal,
         btc_accounts=all_btc_accounts,
-        eth_balances=eth_balances,
-        token_balances=token_balances,
-        btc_balances=['600000000'],
+        eth_balances=eth_balances_after_removal,
+        token_balances=token_balances_after_removal,
+        btc_balances=['3000000', '5000000'],
     )
     # remove the new BTC account
     with setup.bitcoin_patch:
@@ -814,16 +811,16 @@ def test_remove_blockchain_accounts(
     assert json_data['message'] == ''
     assert_btc_balances_result(
         json_data=json_data,
-        btc_accounts=all_btc_accounts,
-        btc_balances=setup.btc_balances,
+        btc_accounts=btc_accounts_after_removal,
+        btc_balances=['5000000'],
         also_eth=True,
     )
     # Also make sure it's removed from the DB
     accounts = rotki.data.db.get_blockchain_accounts()
     assert len(accounts.eth) == 2
-    assert all(acc in accounts.eth for acc in all_eth_accounts)
+    assert all(acc in accounts.eth for acc in eth_accounts_after_removal)
     assert len(accounts.btc) == 1
-    assert all(acc in accounts.btc for acc in all_btc_accounts)
+    assert all(acc in accounts.btc for acc in btc_accounts_after_removal)
 
     # Now try to query all balances to make sure the result is also stored
     with setup.etherscan_patch, setup.bitcoin_patch:
@@ -836,8 +833,8 @@ def test_remove_blockchain_accounts(
     assert json_data['message'] == ''
     assert_btc_balances_result(
         json_data=json_data,
-        btc_accounts=all_btc_accounts,
-        btc_balances=setup.btc_balances,
+        btc_accounts=btc_accounts_after_removal,
+        btc_balances=['5000000'],
         also_eth=True,
     )
 
@@ -860,14 +857,16 @@ def test_remove_blockchain_accounts_async(
 
     # Test by having balances queried before removing an account
     removed_eth_accounts = [ethereum_accounts[0], ethereum_accounts[2]]
-    all_eth_accounts = [ethereum_accounts[1], ethereum_accounts[3]]
-    eth_balances = ['3000000', '4000000']
-    token_balances = {'RDN': ['0', '250000000']}
+    eth_accounts_after_removal = [ethereum_accounts[1], ethereum_accounts[3]]
+    all_eth_balances = ['1000000', '2000000', '3000000', '4000000']
+    token_balances = {'RDN': ['0', '250000000', '450000000', '0']}
+    eth_balances_after_removal = ['2000000', '4000000']
+    token_balances_after_removal = {'RDN': ['250000000', '0']}
     setup = setup_balances(
         rotki,
-        ethereum_accounts=all_eth_accounts,
+        ethereum_accounts=ethereum_accounts,
         btc_accounts=btc_accounts,
-        eth_balances=eth_balances,
+        eth_balances=all_eth_balances,
         token_balances=token_balances,
     )
     with setup.etherscan_patch, setup.bitcoin_patch:
@@ -875,17 +874,11 @@ def test_remove_blockchain_accounts_async(
             rotkehlchen_api_server,
             "blockchainbalancesresource",
         ))
-
-    # Accounts to remove. Assure they are not in order to test that it does not affect anything
-    removed_eth_accounts = [ethereum_accounts[0], ethereum_accounts[2]]
-    all_eth_accounts = [ethereum_accounts[1], ethereum_accounts[3]]
-    eth_balances = ['3000000', '4000000']
-    token_balances = {'RDN': ['0', '250000000']}
     setup = setup_balances(
         rotki,
-        ethereum_accounts=all_eth_accounts,
+        ethereum_accounts=ethereum_accounts,
         btc_accounts=btc_accounts,
-        eth_balances=eth_balances,
+        eth_balances=all_eth_balances,
         token_balances=token_balances,
     )
 
@@ -901,15 +894,15 @@ def test_remove_blockchain_accounts_async(
     assert_eth_balances_result(
         rotki=rotki,
         json_data=outcome,
-        eth_accounts=all_eth_accounts,
-        eth_balances=setup.eth_balances,
-        token_balances=setup.token_balances,
-        also_btc=False,  # All blockchain assets have not been queried yet
+        eth_accounts=eth_accounts_after_removal,
+        eth_balances=eth_balances_after_removal,
+        token_balances=token_balances_after_removal,
+        also_btc=True,  # We queried all balances at the start
     )
     # Also make sure they are removed from the DB
     accounts = rotki.data.db.get_blockchain_accounts()
     assert len(accounts.eth) == 2
-    assert all(acc in accounts.eth for acc in all_eth_accounts)
+    assert all(acc in accounts.eth for acc in eth_accounts_after_removal)
     assert len(accounts.btc) == 2
     assert all(acc in accounts.btc for acc in btc_accounts)
 
@@ -925,21 +918,22 @@ def test_remove_blockchain_accounts_async(
     assert_eth_balances_result(
         rotki=rotki,
         json_data=json_data,
-        eth_accounts=all_eth_accounts,
-        eth_balances=setup.eth_balances,
-        token_balances=setup.token_balances,
+        eth_accounts=eth_accounts_after_removal,
+        eth_balances=eth_balances_after_removal,
+        token_balances=token_balances_after_removal,
         also_btc=True,
     )
 
     # Now we will try to remove a BTC account. Setup the mocking infrastructure again
-    all_btc_accounts = [UNIT_BTC_ADDRESS2]
+    all_btc_accounts = [UNIT_BTC_ADDRESS1, UNIT_BTC_ADDRESS2]
+    btc_accounts_after_removal = [UNIT_BTC_ADDRESS2]
     setup = setup_balances(
         rotki,
-        ethereum_accounts=all_eth_accounts,
+        ethereum_accounts=eth_accounts_after_removal,
         btc_accounts=all_btc_accounts,
-        eth_balances=eth_balances,
-        token_balances=token_balances,
-        btc_balances=['600000000'],
+        eth_balances=eth_balances_after_removal,
+        token_balances=token_balances_after_removal,
+        btc_balances=['3000000', '5000000'],
     )
     # remove the new BTC account
     with setup.bitcoin_patch:
@@ -952,16 +946,16 @@ def test_remove_blockchain_accounts_async(
         outcome = wait_for_async_task(rotkehlchen_api_server, task_id)
     assert_btc_balances_result(
         json_data=outcome,
-        btc_accounts=all_btc_accounts,
-        btc_balances=setup.btc_balances,
+        btc_accounts=btc_accounts_after_removal,
+        btc_balances=['5000000'],
         also_eth=True,
     )
     # Also make sure it's removed from the DB
     accounts = rotki.data.db.get_blockchain_accounts()
     assert len(accounts.eth) == 2
-    assert all(acc in accounts.eth for acc in all_eth_accounts)
+    assert all(acc in accounts.eth for acc in eth_accounts_after_removal)
     assert len(accounts.btc) == 1
-    assert all(acc in accounts.btc for acc in all_btc_accounts)
+    assert all(acc in accounts.btc for acc in btc_accounts_after_removal)
 
     # Now try to query all balances to make sure the result is also stored
     with setup.etherscan_patch, setup.bitcoin_patch:
@@ -974,7 +968,7 @@ def test_remove_blockchain_accounts_async(
     assert json_data['message'] == ''
     assert_btc_balances_result(
         json_data=json_data,
-        btc_accounts=all_btc_accounts,
-        btc_balances=setup.btc_balances,
+        btc_accounts=btc_accounts_after_removal,
+        btc_balances=['5000000'],
         also_eth=True,
     )
diff --git a/rotkehlchen/tests/utils/blockchain.py b/rotkehlchen/tests/utils/blockchain.py
--- a/rotkehlchen/tests/utils/blockchain.py
+++ b/rotkehlchen/tests/utils/blockchain.py
@@ -246,10 +246,12 @@ def assert_btc_balances_result(
     msg = 'given balances and accounts should have same length'
     assert len(btc_accounts) == len(btc_balances), msg
     for idx, account in enumerate(btc_accounts):
-        assert FVal(per_account[account]['amount']) == satoshis_to_btc(
-            FVal(btc_balances[idx]),
-        )
-        assert FVal(per_account[account]['usd_value']) > ZERO
+        balance = satoshis_to_btc(FVal(btc_balances[idx]))
+        assert FVal(per_account[account]['amount']) == balance
+        if balance == ZERO:
+            assert FVal(per_account[account]['usd_value']) == ZERO
+        else:
+            assert FVal(per_account[account]['usd_value']) > ZERO
 
     totals = result['totals']
     if also_eth:
@@ -259,7 +261,10 @@ def assert_btc_balances_result(
 
     expected_btc_total = sum(satoshis_to_btc(FVal(balance)) for balance in btc_balances)
     assert FVal(totals['BTC']['amount']) == expected_btc_total
-    assert FVal(totals['BTC']['usd_value']) > ZERO
+    if expected_btc_total == ZERO:
+        assert FVal(totals['BTC']['usd_value']) == ZERO
+    else:
+        assert FVal(totals['BTC']['usd_value']) > ZERO
 
 
 def assert_eth_balances_result(
@@ -285,11 +290,18 @@ def assert_eth_balances_result(
         per_account = per_account['ETH']
         assert len(per_account) == len(eth_accounts)
         for idx, account in enumerate(eth_accounts):
-            assert FVal(per_account[account]['ETH']) == from_wei(FVal(eth_balances[idx]))
-            assert FVal(per_account[account]['usd_value']) > ZERO
+            balance = from_wei(FVal(eth_balances[idx]))
+            assert FVal(per_account[account]['ETH']) == balance
+            if balance == ZERO:
+                assert FVal(per_account[account]['usd_value']) == ZERO
+            else:
+                assert FVal(per_account[account]['usd_value']) > ZERO
             for symbol, balances in token_balances.items():
                 token_balance = FVal(balances[idx])
-                if token_balance != ZERO:
+                if token_balance == ZERO:
+                    msg = f'{account} should have no entry for {symbol}'
+                    assert symbol not in per_account[account], msg
+                else:
                     assert FVal(per_account[account][symbol]) == from_wei(token_balance)
 
     if totals_only:
@@ -306,7 +318,11 @@ def assert_eth_balances_result(
 
     expected_total_eth = sum(from_wei(FVal(balance)) for balance in eth_balances)
     assert FVal(totals['ETH']['amount']) == expected_total_eth
-    assert FVal(totals['ETH']['usd_value']) > ZERO
+    if expected_total_eth == ZERO:
+        FVal(totals['ETH']['usd_value']) == ZERO
+    else:
+        assert FVal(totals['ETH']['usd_value']) > ZERO
+
     for symbol, balances in token_balances.items():
         if symbol not in rotki.blockchain.owned_eth_tokens:
             # If the token got removed from the owned tokens in the test make sure
diff --git a/rotkehlchen/tests/utils/rotkehlchen.py b/rotkehlchen/tests/utils/rotkehlchen.py
--- a/rotkehlchen/tests/utils/rotkehlchen.py
+++ b/rotkehlchen/tests/utils/rotkehlchen.py
@@ -79,7 +79,7 @@ def setup_balances(
             'The btc balances should be a list with each '
             'element representing balance of an account'
         )
-        assert len(eth_balances) == len(ethereum_accounts)
+        assert len(btc_balances) == len(btc_accounts)
     else:
         # Default test values
         if len(btc_accounts) != 0: