Repository: rotki/rotki

Issue:
Greenlet for task 9 dies with exception: unsupported operand type(s) for -: 'NoneType' and 'int'
## Problem Definition

After update can no longer update balances

## Logs

```{log}
[21/07/2022 13:00:32 EDT] ERROR rotkehlchen.api.rest Greenlet with id 4486557008: Greenlet for task 9 dies with exception: unsupported operand type(s) for -: 'NoneType' and 'int'.
Exception Name: <class 'TypeError'>
Exception Info: unsupported operand type(s) for -: 'NoneType' and 'int'
Traceback:
   File "src/gevent/greenlet.py", line 906, in gevent._gevent_cgreenlet.Greenlet.run
  File "rotkehlchen/api/rest.py", line 274, in _do_query_async
  File "rotkehlchen/api/rest.py", line 645, in _query_blockchain_balances
  File "rotkehlchen/utils/mixins/lockable.py", line 46, in wrapper
  File "rotkehlchen/utils/mixins/cacheable.py", line 97, in wrapper
  File "rotkehlchen/chain/manager.py", line 629, in query_balances
  File "rotkehlchen/utils/mixins/lockable.py", line 46, in wrapper
  File "rotkehlchen/utils/mixins/cacheable.py", line 97, in wrapper
  File "rotkehlchen/chain/manager.py", line 1572, in query_ethereum_balances
  File "rotkehlchen/chain/manager.py", line 1520, in query_ethereum_tokens
  File "rotkehlchen/chain/manager.py", line 1489, in _query_ethereum_tokens
  File "rotkehlchen/chain/ethereum/tokens.py", line 163, in query_tokens_for_addresses
  File "rotkehlchen/chain/ethereum/tokens.py", line 82, in detect_tokens_for_address
  File "rotkehlchen/chain/ethereum/tokens.py", line 213, in _get_tokens_balance_and_price
  File "rotkehlchen/inquirer.py", line 503, in find_usd_price
  File "rotkehlchen/inquirer.py", line 358, in _query_oracle_instances
  File "rotkehlchen/chain/ethereum/oracles/uniswap.py", line 275, in query_current_price
  File "rotkehlchen/chain/ethereum/oracles/uniswap.py", line 241, in get_price
  File "rotkehlchen/chain/ethereum/oracles/uniswap.py", line 433, in get_pool_price
```

### System Description

App Version: 1.25.0
Frontend Version: 
Platform: darwin x64 12.4.0
Electron: 18.2.3




Discussion:
Hey @NoahMarconi thank you for reporting this! It would really helps us to solve the problem if you could share full logs or ethereum address(es) that you are tracking with one of the team members. 
You can do it in our Discord: https://discord.gg/kDzUFc7R (note: we have captcha, so have DMs open to pass it).

Resolving patch:
diff --git a/rotkehlchen/chain/ethereum/oracles/uniswap.py b/rotkehlchen/chain/ethereum/oracles/uniswap.py
--- a/rotkehlchen/chain/ethereum/oracles/uniswap.py
+++ b/rotkehlchen/chain/ethereum/oracles/uniswap.py
@@ -361,6 +361,10 @@ def get_pool_price(
             to_checksum_address(pool_contract.decode(output[2], 'token1')[0]),  # noqa: E501 pylint:disable=unsubscriptable-object
         )
         sqrt_price_x96, _, _, _, _, _, _ = pool_contract.decode(output[0], 'slot0')
+        if token_0.decimals is None:
+            raise DefiPoolError(f'Token {token_0} has None as decimals')
+        if token_1.decimals is None:
+            raise DefiPoolError(f'Token {token_1} has None as decimals')
         decimals_constant = 10 ** (token_0.decimals - token_1.decimals)
 
         price = FVal((sqrt_price_x96 * sqrt_price_x96) / 2 ** (192) * decimals_constant)
@@ -429,6 +433,10 @@ def get_pool_price(
         token_1 = EthereumToken(
             to_checksum_address(pool_contract.decode(output[2], 'token1')[0]),  # noqa: E501 pylint:disable=unsubscriptable-object
         )
+        if token_0.decimals is None:
+            raise DefiPoolError(f'Token {token_0} has None as decimals')
+        if token_1.decimals is None:
+            raise DefiPoolError(f'Token {token_1} has None as decimals')
         reserve_0, reserve_1, _ = pool_contract.decode(output[0], 'getReserves')
         decimals_constant = 10**(token_0.decimals - token_1.decimals)
 


Test patch:
diff --git a/rotkehlchen/tests/unit/test_defi_oracles.py b/rotkehlchen/tests/unit/test_defi_oracles.py
--- a/rotkehlchen/tests/unit/test_defi_oracles.py
+++ b/rotkehlchen/tests/unit/test_defi_oracles.py
@@ -1,8 +1,13 @@
+from copy import deepcopy
+from unittest.mock import patch
+
 import pytest
 
-from rotkehlchen.assets.asset import Asset
-from rotkehlchen.constants.assets import A_1INCH, A_BTC, A_DOGE, A_ETH, A_LINK, A_WETH
+from rotkehlchen.assets.asset import Asset, EthereumToken
+from rotkehlchen.assets.resolver import AssetResolver
+from rotkehlchen.constants.assets import A_1INCH, A_BTC, A_DOGE, A_ETH, A_LINK, A_USDC, A_WETH
 from rotkehlchen.constants.misc import ONE, ZERO
+from rotkehlchen.errors.defi import DefiPoolError
 from rotkehlchen.errors.price import PriceQueryUnsupportedAsset
 from rotkehlchen.fval import FVal
 from rotkehlchen.inquirer import CurrentPriceOracle
@@ -55,3 +60,32 @@ def test_uniswap_oracles_special_cases(inquirer_defi):
             inquirer_defi._uniswapv2.query_current_price(A_BTC, A_DOGE)
         # Same asset
         assert inquirer_defi._uniswapv2.query_current_price(A_ETH, A_WETH) == Price(ONE)
+
+
+@pytest.mark.parametrize('use_clean_caching_directory', [True])
+@pytest.mark.parametrize('should_mock_current_price_queries', [False])
+def test_uniswap_no_decimals(inquirer_defi):
+    """Test that if a token has no information about the number of decimals a proper error
+    is raised"""
+    asset_resolver = AssetResolver()
+    original_getter = asset_resolver.get_asset_data
+
+    def fake_weth_token():
+        """Make sure that the weth token has no decimals fields and any other token
+        is loaded properly
+        """
+        def mocked_asset_getter(asset_identifier: str, form_with_incomplete_data: bool = False):
+            if asset_identifier == A_WETH.identifier:
+                fake_weth = deepcopy(A_WETH)
+                object.__setattr__(fake_weth, 'decimals', None)
+                return fake_weth
+            return original_getter(asset_identifier, form_with_incomplete_data)
+        return patch.object(asset_resolver, 'get_asset_data', wraps=mocked_asset_getter)
+
+    with fake_weth_token():
+        weth = EthereumToken(A_WETH.ethereum_address)
+        assert weth.decimals is None
+        with pytest.raises(DefiPoolError):
+            inquirer_defi._uniswapv2.query_current_price(weth, A_USDC)
+        with pytest.raises(DefiPoolError):
+            inquirer_defi._uniswapv3.query_current_price(weth, A_USDC)