Repository: rotki/rotki

Issue:
Etherscan API key is not always used.
## Problem Definition

There is a bug in the way etherscan calls are made. Half of the calls don't actually use the provided API key which makes the chain querying experience very slow.

## Task 

Fix it.


Discussion:

Resolving patch:
diff --git a/rotkehlchen/externalapis/etherscan.py b/rotkehlchen/externalapis/etherscan.py
--- a/rotkehlchen/externalapis/etherscan.py
+++ b/rotkehlchen/externalapis/etherscan.py
@@ -83,6 +83,7 @@ def __init__(self, database: DBHandler, msg_aggregator: MessagesAggregator) -> N
         super().__init__(database=database, service_name=ExternalService.ETHERSCAN)
         self.msg_aggregator = msg_aggregator
         self.session = requests.session()
+        self.warning_given = False
         self.session.headers.update({'User-Agent': 'rotkehlchen'})
 
     @overload  # noqa: F811
@@ -152,11 +153,17 @@ def _query(  # noqa: F811
 
         api_key = self._get_api_key()
         if api_key is None:
-            raise RemoteError(
-                'Etherscan has introduced compulsory API keys.'
-                'Please go to to https://etherscan.io/register, create an API '
-                'key and then input it in the external service credentials setting of Rotki',
-            )
+            if not self.warning_given:
+                self.msg_aggregator.add_warning(
+                    'You do not have an Etherscan API key configured. Rotki '
+                    'etherscan queries will still work but will be very slow. '
+                    'If you are not using your own ethereum node, it iss recommended '
+                    'to go to https://etherscan.io/register, create an API '
+                    'key and then input it in the external service credentials setting of Rotki',
+                )
+                self.warning_given = True
+        else:
+            query_str += f'&apikey={api_key}'
 
         logger.debug(f'Querying etherscan: {query_str}')
         backoff = 1


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
@@ -556,43 +556,18 @@ def test_add_blockchain_accounts(
 
 
 @pytest.mark.parametrize('include_etherscan_key', [False])
-def test_add_blockchain_accounts_synchronously_no_etherscan(rotkehlchen_api_server):
-    """Make sure that adding an ethereum blockchain account without etherscan key returns error
-
-    Regression test for https://github.com/rotki/rotki/issues/852
-    """
+def test_no_etherscan_is_detected(rotkehlchen_api_server):
+    """Make sure that interacting with ethereum without an etherscan key is given a warning"""
+    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
     response = requests.put(api_url_for(
         rotkehlchen_api_server,
         "blockchainsaccountsresource",
         blockchain='ETH',
     ), json={'accounts': [{'address': make_ethereum_address()}]})
-    assert_error_response(
-        response=response,
-        contained_in_msg='Etherscan has introduced compulsory API keys.',
-        status_code=HTTPStatus.BAD_GATEWAY,
-    )
-
-
-@pytest.mark.parametrize('include_etherscan_key', [False])
-@pytest.mark.parametrize('number_of_eth_accounts', [2])
-def test_remove_blockchain_accounts_synchronously_no_etherscan(
-        rotkehlchen_api_server,
-        ethereum_accounts,
-):
-    """Make sure that removing an ethereum blockchain account without etherscan key returns error
-
-    Regression test for https://github.com/rotki/rotki/issues/852
-    """
-    response = requests.delete(api_url_for(
-        rotkehlchen_api_server,
-        "blockchainsaccountsresource",
-        blockchain='ETH',
-    ), json={'accounts': [ethereum_accounts[0]]})
-    assert_error_response(
-        response=response,
-        contained_in_msg='Etherscan has introduced compulsory API keys.',
-        status_code=HTTPStatus.BAD_GATEWAY,
-    )
+    assert_proper_response(response)
+    warnings = rotki.msg_aggregator.consume_warnings()
+    assert len(warnings) == 1
+    assert 'You do not have an Etherscan API key configured' in warnings[0]
 
 
 @pytest.mark.parametrize('number_of_eth_accounts', [2])
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
@@ -391,6 +391,9 @@ def mock_requests_get(url, *args, **kwargs):
             contract = web3.eth.contract(address=ETH_SCAN_ADDRESS, abi=ETH_SCAN_ABI)
             if 'data=0xdbdbb51b' in url:  # Eth balance query
                 data = url.split('data=')[1]
+                if '&apikey' in data:
+                    data = data.split('&apikey')[0]
+
                 fn_abi = contract._find_matching_fn_abi(
                     fn_identifier='etherBalances',
                     args=[list(eth_map.keys())],
@@ -406,6 +409,8 @@ def mock_requests_get(url, *args, **kwargs):
                 response = f'{{"jsonrpc":"2.0","id":1,"result":"{result}"}}'
             elif 'data=0x06187b4f' in url:  # Multi token balance query
                 data = url.split('data=')[1]
+                if '&apikey' in data:
+                    data = data.split('&apikey')[0]
                 # not really the given args, but we just want the fn abi
                 args = [list(eth_map.keys()), list(eth_map.keys())]
                 fn_abi = contract._find_matching_fn_abi(