prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Etherscan balance query for more than 20 accounts won't work
  Etherscan has a 20 account limit per query.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `rotkehlchen/ethchain.py`
  Content:
  ```
  1 import os
  2 from web3 import Web3, HTTPProvider
  3 from requests import ConnectionError
  4 
  5 from rotkehlchen.utils import from_wei, rlk_jsonloads, request_get
  6 from rotkehlchen.fval import FVal
  7 
  8 import logging
  9 logger = logging.getLogger(__name__)
  10 
  11 
  12 class Ethchain(object):
  13     def __init__(self, ethrpc_port, attempt_connect=True):
  14         self.web3 = None
  15         self.rpc_port = ethrpc_port
  16         self.connected = False
  17         if attempt_connect:
  18             self.attempt_connect(ethrpc_port)
  19 
  20     def attempt_connect(self, ethrpc_port, mainnet_check=True):
  21         if self.rpc_port == ethrpc_port and self.connected:
  22             # We are already connected
  23             return True, 'Already connected to an ethereum node'
  24 
  25         if self.web3:
  26             del self.web3
  27 
  28         try:
  29             self.web3 = Web3(HTTPProvider('http://localhost:{}'.format(ethrpc_port)))
  30         except ConnectionError:
  31             logger.warn('Could not connect to a local ethereum node. Will use etherscan only')
  32             self.connected = False
  33             return False, 'Failed to connect to ethereum node at port {}'.format(ethrpc_port)
  34 
  35         if self.web3.isConnected():
  36             dir_path = os.path.dirname(os.path.realpath(__file__))
  37             with open(os.path.join(dir_path, 'data', 'token_abi.json'), 'r') as f:
  38                 self.token_abi = rlk_jsonloads(f.read())
  39 
  40             # Also make sure we are actually connected to the Ethereum mainnet
  41             if mainnet_check:
  42                 genesis_hash = self.web3.eth.getBlock(0)['hash'].hex()
  43                 target = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'
  44                 if genesis_hash != target:
  45                     logger.warn(
  46                         'Connected to a local ethereum node but it is not on the ethereum mainnet'
  47                     )
  48                     self.connected = False
  49                     message = (
  50                         'Connected to ethereum node at port {} but it is not on '
  51                         'the ethereum mainnet'.format(ethrpc_port)
  52                     )
  53                     return False, message
  54 
  55             self.connected = True
  56             return True, ''
  57         else:
  58             logger.warn('Could not connect to a local ethereum node. Will use etherscan only')
  59             self.connected = False
  60             message = 'Failed to connect to ethereum node at port {}'.format(ethrpc_port)
  61 
  62         # If we get here we did not connnect
  63         return False, message
  64 
  65     def set_rpc_port(self, port):
  66         """ Attempts to set the RPC port for the ethereum client.
  67 
  68         Returns a tuple (result, message)
  69             - result: Boolean for success or failure of changing the rpc port
  70             - message: A message containing information on what happened. Can
  71                        be populated both in case of success or failure"""
  72         result, message = self.attempt_connect(port)
  73         if result:
  74             self.ethrpc_port = port
  75         return result, message
  76 
  77     def get_eth_balance(self, account):
  78         if not self.connected:
  79             eth_resp = request_get(
  80                 'https://api.etherscan.io/api?module=account&action=balance&address=%s'
  81                 % account
  82             )
  83             if eth_resp['status'] != 1:
  84                 raise ValueError('Failed to query etherscan for accounts balance')
  85             amount = FVal(eth_resp['result'])
  86             return from_wei(amount)
  87         else:
  88             return from_wei(self.web3.eth.getBalance(account))
  89 
  90     def get_multieth_balance(self, accounts):
  91         """Returns a dict with keys being accounts and balances in ETH"""
  92         balances = {}
  93         if not self.connected:
  94             # TODO: accounts.length should be less than 20. If more we gotta do
  95             # multiple calls
  96             eth_resp = request_get(
  97                 'https://api.etherscan.io/api?module=account&action=balancemulti&address=%s' %
  98                 ','.join(accounts)
  99             )
  100             if eth_resp['status'] != 1:
  101                 raise ValueError('Failed to query etherscan for accounts balance')
  102             eth_accounts = eth_resp['result']
  103             for account_entry in eth_accounts:
  104                 amount = FVal(account_entry['balance'])
  105                 balances[account_entry['account']] = from_wei(amount)
  106 
  107         else:
  108             for account in accounts:
  109                 amount = FVal(self.web3.eth.getBalance(account))
  110                 balances[account] = from_wei(amount)
  111 
  112         return balances
  113 
  114     def get_multitoken_balance(self, token_symbol, token_address, token_decimals, accounts):
  115         """Return a dictionary with keys being accounts and value balances of token
  116         Balance value is normalized through the token decimals.
  117         """
  118         balances = {}
  119         if self.connected:
  120             token_contract = self.web3.eth.contract(
  121                 address=token_address,
  122                 abi=self.token_abi
  123             )
  124             for account in accounts:
  125                 token_amount = FVal(token_contract.functions.balanceOf(account).call())
  126                 if token_amount != 0:
  127                     balances[account] = token_amount / (FVal(10) ** FVal(token_decimals))
  128         else:
  129             for account in accounts:
  130                 print('Checking token {} for account {}'.format(token_symbol, account))
  131                 resp = request_get(
  132                     'https://api.etherscan.io/api?module=account&action='
  133                     'tokenbalance&contractaddress={}&address={}'.format(
  134                         token_address,
  135                         account,
  136                     ))
  137                 if resp['status'] != 1:
  138                     raise ValueError(
  139                         'Failed to query etherscan for {} token balance of {}'.format(
  140                             token_symbol,
  141                             account,
  142                         ))
  143                 token_amount = FVal(resp['result'])
  144                 if token_amount != 0:
  145                     balances[account] = token_amount / (FVal(10) ** FVal(token_decimals))
  146 
  147         return balances
  148 
  149     def get_token_balance(self, token_symbol, token_address, token_decimals, account):
  150         res = self.get_multitoken_balance(token_symbol, token_address, token_decimals, [account])
  151         return res.get(account, 0)
  152 
  153     def get_block_by_number(self, num):
  154         if not self.connected:
  155             return None
  156 
  157         return self.web3.eth.getBlock(num)
  158
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/rotkehlchen/ethchain.py b/rotkehlchen/ethchain.py
  --- a/rotkehlchen/ethchain.py
  +++ b/rotkehlchen/ethchain.py
  @@ -90,19 +90,25 @@
       def get_multieth_balance(self, accounts):
           """Returns a dict with keys being accounts and balances in ETH"""
           balances = {}
  +
           if not self.connected:
  -            # TODO: accounts.length should be less than 20. If more we gotta do
  -            # multiple calls
  -            eth_resp = request_get(
  -                'https://api.etherscan.io/api?module=account&action=balancemulti&address=%s' %
  -                ','.join(accounts)
  -            )
  -            if eth_resp['status'] != 1:
  -                raise ValueError('Failed to query etherscan for accounts balance')
  -            eth_accounts = eth_resp['result']
  -            for account_entry in eth_accounts:
  -                amount = FVal(account_entry['balance'])
  -                balances[account_entry['account']] = from_wei(amount)
  +            if len(accounts) > 20:
  +                new_accounts = [accounts[x:x+2] for x in range(0, len(accounts), 2)]
  +            else:
  +                new_accounts = [accounts]
  +
  +            for account_slice in new_accounts:
  +                eth_resp = request_get(
  +                    'https://api.etherscan.io/api?module=account&action=balancemulti&address=%s' %
  +                    ','.join(account_slice)
  +                )
  +                if eth_resp['status'] != 1:
  +                    raise ValueError('Failed to query etherscan for accounts balance')
  +                eth_accounts = eth_resp['result']
  +
  +                for account_entry in eth_accounts:
  +                    amount = FVal(account_entry['balance'])
  +                    balances[account_entry['account']] = from_wei(amount)
   
           else:
               for account in accounts:
  
verification_info: {"golden_diff": "diff --git a/rotkehlchen/ethchain.py b/rotkehlchen/ethchain.py\n--- a/rotkehlchen/ethchain.py\n+++ b/rotkehlchen/ethchain.py\n@@ -90,19 +90,25 @@\n     def get_multieth_balance(self, accounts):\n         \"\"\"Returns a dict with keys being accounts and balances in ETH\"\"\"\n         balances = {}\n+\n         if not self.connected:\n-            # TODO: accounts.length should be less than 20. If more we gotta do\n-            # multiple calls\n-            eth_resp = request_get(\n-                'https://api.etherscan.io/api?module=account&action=balancemulti&address=%s' %\n-                ','.join(accounts)\n-            )\n-            if eth_resp['status'] != 1:\n-                raise ValueError('Failed to query etherscan for accounts balance')\n-            eth_accounts = eth_resp['result']\n-            for account_entry in eth_accounts:\n-                amount = FVal(account_entry['balance'])\n-                balances[account_entry['account']] = from_wei(amount)\n+            if len(accounts) > 20:\n+                new_accounts = [accounts[x:x+2] for x in range(0, len(accounts), 2)]\n+            else:\n+                new_accounts = [accounts]\n+\n+            for account_slice in new_accounts:\n+                eth_resp = request_get(\n+                    'https://api.etherscan.io/api?module=account&action=balancemulti&address=%s' %\n+                    ','.join(account_slice)\n+                )\n+                if eth_resp['status'] != 1:\n+                    raise ValueError('Failed to query etherscan for accounts balance')\n+                eth_accounts = eth_resp['result']\n+\n+                for account_entry in eth_accounts:\n+                    amount = FVal(account_entry['balance'])\n+                    balances[account_entry['account']] = from_wei(amount)\n \n         else:\n             for account in accounts:\n", "issue": "Etherscan balance query for more than 20 accounts won't work\nEtherscan has a 20 account limit per query.\n", "before_files": [{"content": "import os\nfrom web3 import Web3, HTTPProvider\nfrom requests import ConnectionError\n\nfrom rotkehlchen.utils import from_wei, rlk_jsonloads, request_get\nfrom rotkehlchen.fval import FVal\n\nimport logging\nlogger = logging.getLogger(__name__)\n\n\nclass Ethchain(object):\n    def __init__(self, ethrpc_port, attempt_connect=True):\n        self.web3 = None\n        self.rpc_port = ethrpc_port\n        self.connected = False\n        if attempt_connect:\n            self.attempt_connect(ethrpc_port)\n\n    def attempt_connect(self, ethrpc_port, mainnet_check=True):\n        if self.rpc_port == ethrpc_port and self.connected:\n            # We are already connected\n            return True, 'Already connected to an ethereum node'\n\n        if self.web3:\n            del self.web3\n\n        try:\n            self.web3 = Web3(HTTPProvider('http://localhost:{}'.format(ethrpc_port)))\n        except ConnectionError:\n            logger.warn('Could not connect to a local ethereum node. Will use etherscan only')\n            self.connected = False\n            return False, 'Failed to connect to ethereum node at port {}'.format(ethrpc_port)\n\n        if self.web3.isConnected():\n            dir_path = os.path.dirname(os.path.realpath(__file__))\n            with open(os.path.join(dir_path, 'data', 'token_abi.json'), 'r') as f:\n                self.token_abi = rlk_jsonloads(f.read())\n\n            # Also make sure we are actually connected to the Ethereum mainnet\n            if mainnet_check:\n                genesis_hash = self.web3.eth.getBlock(0)['hash'].hex()\n                target = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'\n                if genesis_hash != target:\n                    logger.warn(\n                        'Connected to a local ethereum node but it is not on the ethereum mainnet'\n                    )\n                    self.connected = False\n                    message = (\n                        'Connected to ethereum node at port {} but it is not on '\n                        'the ethereum mainnet'.format(ethrpc_port)\n                    )\n                    return False, message\n\n            self.connected = True\n            return True, ''\n        else:\n            logger.warn('Could not connect to a local ethereum node. Will use etherscan only')\n            self.connected = False\n            message = 'Failed to connect to ethereum node at port {}'.format(ethrpc_port)\n\n        # If we get here we did not connnect\n        return False, message\n\n    def set_rpc_port(self, port):\n        \"\"\" Attempts to set the RPC port for the ethereum client.\n\n        Returns a tuple (result, message)\n            - result: Boolean for success or failure of changing the rpc port\n            - message: A message containing information on what happened. Can\n                       be populated both in case of success or failure\"\"\"\n        result, message = self.attempt_connect(port)\n        if result:\n            self.ethrpc_port = port\n        return result, message\n\n    def get_eth_balance(self, account):\n        if not self.connected:\n            eth_resp = request_get(\n                'https://api.etherscan.io/api?module=account&action=balance&address=%s'\n                % account\n            )\n            if eth_resp['status'] != 1:\n                raise ValueError('Failed to query etherscan for accounts balance')\n            amount = FVal(eth_resp['result'])\n            return from_wei(amount)\n        else:\n            return from_wei(self.web3.eth.getBalance(account))\n\n    def get_multieth_balance(self, accounts):\n        \"\"\"Returns a dict with keys being accounts and balances in ETH\"\"\"\n        balances = {}\n        if not self.connected:\n            # TODO: accounts.length should be less than 20. If more we gotta do\n            # multiple calls\n            eth_resp = request_get(\n                'https://api.etherscan.io/api?module=account&action=balancemulti&address=%s' %\n                ','.join(accounts)\n            )\n            if eth_resp['status'] != 1:\n                raise ValueError('Failed to query etherscan for accounts balance')\n            eth_accounts = eth_resp['result']\n            for account_entry in eth_accounts:\n                amount = FVal(account_entry['balance'])\n                balances[account_entry['account']] = from_wei(amount)\n\n        else:\n            for account in accounts:\n                amount = FVal(self.web3.eth.getBalance(account))\n                balances[account] = from_wei(amount)\n\n        return balances\n\n    def get_multitoken_balance(self, token_symbol, token_address, token_decimals, accounts):\n        \"\"\"Return a dictionary with keys being accounts and value balances of token\n        Balance value is normalized through the token decimals.\n        \"\"\"\n        balances = {}\n        if self.connected:\n            token_contract = self.web3.eth.contract(\n                address=token_address,\n                abi=self.token_abi\n            )\n            for account in accounts:\n                token_amount = FVal(token_contract.functions.balanceOf(account).call())\n                if token_amount != 0:\n                    balances[account] = token_amount / (FVal(10) ** FVal(token_decimals))\n        else:\n            for account in accounts:\n                print('Checking token {} for account {}'.format(token_symbol, account))\n                resp = request_get(\n                    'https://api.etherscan.io/api?module=account&action='\n                    'tokenbalance&contractaddress={}&address={}'.format(\n                        token_address,\n                        account,\n                    ))\n                if resp['status'] != 1:\n                    raise ValueError(\n                        'Failed to query etherscan for {} token balance of {}'.format(\n                            token_symbol,\n                            account,\n                        ))\n                token_amount = FVal(resp['result'])\n                if token_amount != 0:\n                    balances[account] = token_amount / (FVal(10) ** FVal(token_decimals))\n\n        return balances\n\n    def get_token_balance(self, token_symbol, token_address, token_decimals, account):\n        res = self.get_multitoken_balance(token_symbol, token_address, token_decimals, [account])\n        return res.get(account, 0)\n\n    def get_block_by_number(self, num):\n        if not self.connected:\n            return None\n\n        return self.web3.eth.getBlock(num)\n", "path": "rotkehlchen/ethchain.py"}], "after_files": [{"content": "import os\nfrom web3 import Web3, HTTPProvider\nfrom requests import ConnectionError\n\nfrom rotkehlchen.utils import from_wei, rlk_jsonloads, request_get\nfrom rotkehlchen.fval import FVal\n\nimport logging\nlogger = logging.getLogger(__name__)\n\n\nclass Ethchain(object):\n    def __init__(self, ethrpc_port, attempt_connect=True):\n        self.web3 = None\n        self.rpc_port = ethrpc_port\n        self.connected = False\n        if attempt_connect:\n            self.attempt_connect(ethrpc_port)\n\n    def attempt_connect(self, ethrpc_port, mainnet_check=True):\n        if self.rpc_port == ethrpc_port and self.connected:\n            # We are already connected\n            return True, 'Already connected to an ethereum node'\n\n        if self.web3:\n            del self.web3\n\n        try:\n            self.web3 = Web3(HTTPProvider('http://localhost:{}'.format(ethrpc_port)))\n        except ConnectionError:\n            logger.warn('Could not connect to a local ethereum node. Will use etherscan only')\n            self.connected = False\n            return False, 'Failed to connect to ethereum node at port {}'.format(ethrpc_port)\n\n        if self.web3.isConnected():\n            dir_path = os.path.dirname(os.path.realpath(__file__))\n            with open(os.path.join(dir_path, 'data', 'token_abi.json'), 'r') as f:\n                self.token_abi = rlk_jsonloads(f.read())\n\n            # Also make sure we are actually connected to the Ethereum mainnet\n            if mainnet_check:\n                genesis_hash = self.web3.eth.getBlock(0)['hash'].hex()\n                target = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'\n                if genesis_hash != target:\n                    logger.warn(\n                        'Connected to a local ethereum node but it is not on the ethereum mainnet'\n                    )\n                    self.connected = False\n                    message = (\n                        'Connected to ethereum node at port {} but it is not on '\n                        'the ethereum mainnet'.format(ethrpc_port)\n                    )\n                    return False, message\n\n            self.connected = True\n            return True, ''\n        else:\n            logger.warn('Could not connect to a local ethereum node. Will use etherscan only')\n            self.connected = False\n            message = 'Failed to connect to ethereum node at port {}'.format(ethrpc_port)\n\n        # If we get here we did not connnect\n        return False, message\n\n    def set_rpc_port(self, port):\n        \"\"\" Attempts to set the RPC port for the ethereum client.\n\n        Returns a tuple (result, message)\n            - result: Boolean for success or failure of changing the rpc port\n            - message: A message containing information on what happened. Can\n                       be populated both in case of success or failure\"\"\"\n        result, message = self.attempt_connect(port)\n        if result:\n            self.ethrpc_port = port\n        return result, message\n\n    def get_eth_balance(self, account):\n        if not self.connected:\n            eth_resp = request_get(\n                'https://api.etherscan.io/api?module=account&action=balance&address=%s'\n                % account\n            )\n            if eth_resp['status'] != 1:\n                raise ValueError('Failed to query etherscan for accounts balance')\n            amount = FVal(eth_resp['result'])\n            return from_wei(amount)\n        else:\n            return from_wei(self.web3.eth.getBalance(account))\n\n    def get_multieth_balance(self, accounts):\n        \"\"\"Returns a dict with keys being accounts and balances in ETH\"\"\"\n        balances = {}\n\n        if not self.connected:\n            if len(accounts) > 20:\n                new_accounts = [accounts[x:x+2] for x in range(0, len(accounts), 2)]\n            else:\n                new_accounts = [accounts]\n\n            for account_slice in new_accounts:\n                eth_resp = request_get(\n                    'https://api.etherscan.io/api?module=account&action=balancemulti&address=%s' %\n                    ','.join(account_slice)\n                )\n                if eth_resp['status'] != 1:\n                    raise ValueError('Failed to query etherscan for accounts balance')\n                eth_accounts = eth_resp['result']\n\n                for account_entry in eth_accounts:\n                    amount = FVal(account_entry['balance'])\n                    balances[account_entry['account']] = from_wei(amount)\n\n        else:\n            for account in accounts:\n                amount = FVal(self.web3.eth.getBalance(account))\n                balances[account] = from_wei(amount)\n\n        return balances\n\n    def get_multitoken_balance(self, token_symbol, token_address, token_decimals, accounts):\n        \"\"\"Return a dictionary with keys being accounts and value balances of token\n        Balance value is normalized through the token decimals.\n        \"\"\"\n        balances = {}\n        if self.connected:\n            token_contract = self.web3.eth.contract(\n                address=token_address,\n                abi=self.token_abi\n            )\n            for account in accounts:\n                token_amount = FVal(token_contract.functions.balanceOf(account).call())\n                if token_amount != 0:\n                    balances[account] = token_amount / (FVal(10) ** FVal(token_decimals))\n        else:\n            for account in accounts:\n                print('Checking token {} for account {}'.format(token_symbol, account))\n                resp = request_get(\n                    'https://api.etherscan.io/api?module=account&action='\n                    'tokenbalance&contractaddress={}&address={}'.format(\n                        token_address,\n                        account,\n                    ))\n                if resp['status'] != 1:\n                    raise ValueError(\n                        'Failed to query etherscan for {} token balance of {}'.format(\n                            token_symbol,\n                            account,\n                        ))\n                token_amount = FVal(resp['result'])\n                if token_amount != 0:\n                    balances[account] = token_amount / (FVal(10) ** FVal(token_decimals))\n\n        return balances\n\n    def get_token_balance(self, token_symbol, token_address, token_decimals, account):\n        res = self.get_multitoken_balance(token_symbol, token_address, token_decimals, [account])\n        return res.get(account, 0)\n\n    def get_block_by_number(self, num):\n        if not self.connected:\n            return None\n\n        return self.web3.eth.getBlock(num)\n", "path": "rotkehlchen/ethchain.py"}]}