We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
PnL Report is wrong for Genesis ETH2 Validator
## Problem Definition

For a validator that deposited prior to the Beacon Chain Genesis date
- the post genesis deposit event (from the 06/11/2020) is treated as if the 32 ETH were sold
- the genesis day `ETH2 staking daily PnL` value (from the 01/12/2020) in the Profit and Loss Report is too large by 32 ETH (32.016 ETH instead of 0.016 ETH) 

![Bildschirmfoto 2023-01-13 um 00 04 16](https://user-images.githubusercontent.com/20623991/212202315-827637c4-e4ae-4ab5-92fd-4dc3b91142c4.png)

One day later, the `ETH2 staking daily PnL` value is correctly calculated.
![Bildschirmfoto 2023-01-13 um 00 06 01](https://user-images.githubusercontent.com/20623991/212202331-60fe1a24-2266-4fd5-9d88-f92bd004e67f.png)

## Logs

I can provide logs if needed via a DM.

I assume that you can reproduce for any pre-geneis deposited validator. Accordingly, I suggest that you pick a validator with a sufficiently low validator index, e.g. [Validator 999](https://beaconcha.in/validator/999) who [deposited on 05/12/2020](https://etherscan.io/tx/0x187bef85f7797f4f42534fcfa080ed28ab77491b79fe9e9be8039416eebab6bc)


### System Description

Operating system: MacOS Montery
Rotki version: 1.26.3


--- 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/chain/ethereum/modules/eth2/utils.py`
Content:
```
1 import logging
2 from http import HTTPStatus
3 from typing import NamedTuple
4 
5 import gevent
6 import requests
7 from bs4 import BeautifulSoup, SoupStrainer
8 
9 from rotkehlchen.constants.assets import A_ETH
10 from rotkehlchen.constants.misc import ZERO
11 from rotkehlchen.constants.timing import DAY_IN_SECONDS, DEFAULT_TIMEOUT_TUPLE
12 from rotkehlchen.errors.misc import RemoteError
13 from rotkehlchen.fval import FVal
14 from rotkehlchen.history.price import query_usd_price_zero_if_error
15 from rotkehlchen.logging import RotkehlchenLogsAdapter
16 from rotkehlchen.types import Timestamp
17 from rotkehlchen.user_messages import MessagesAggregator
18 from rotkehlchen.utils.misc import create_timestamp
19 
20 from .structures import ValidatorDailyStats
21 
22 logger = logging.getLogger(__name__)
23 log = RotkehlchenLogsAdapter(logger)
24 
25 
26 class ValidatorBalance(NamedTuple):
27     epoch: int
28     balance: int  # in gwei
29     effective_balance: int  # in wei
30 
31 
32 def _parse_fval(line: str, entry: str) -> FVal:
33     try:
34         result = FVal(line.replace('ETH', ''))
35     except ValueError as e:
36         raise RemoteError(f'Could not parse {line} as a number for {entry}') from e
37 
38     return result
39 
40 
41 def _parse_int(line: str, entry: str) -> int:
42     try:
43         if line == '-':
44             result = 0
45         else:
46             result = int(line)
47     except ValueError as e:
48         raise RemoteError(f'Could not parse {line} as an integer for {entry}') from e
49 
50     return result
51 
52 
53 def scrape_validator_daily_stats(
54         validator_index: int,
55         last_known_timestamp: Timestamp,
56         msg_aggregator: MessagesAggregator,
57 ) -> list[ValidatorDailyStats]:
58     """Scrapes the website of beaconcha.in and parses the data directly out of the data table.
59 
60     The parser is very simple. And can break if they change stuff in the way
61     it's displayed in https://beaconcha.in/validator/33710/stats. If that happpens
62     we need to adjust here. If we also somehow programatically get the data in a CSV
63     that would be swell.
64 
65     May raise:
66     - RemoteError if we can't query beaconcha.in or if the data is not in the expected format
67     """
68     url = f'https://beaconcha.in/validator/{validator_index}/stats'
69     tries = 1
70     max_tries = 3
71     backoff = 60
72     while True:
73         log.debug(f'Querying beaconcha.in stats: {url}')
74         try:
75             response = requests.get(url, timeout=DEFAULT_TIMEOUT_TUPLE)
76         except requests.exceptions.RequestException as e:
77             raise RemoteError(f'Beaconcha.in api request {url} failed due to {str(e)}') from e
78 
79         if response.status_code == HTTPStatus.TOO_MANY_REQUESTS and tries <= max_tries:
80             sleep_secs = backoff * tries / max_tries
81             log.warning(
82                 f'Querying {url} returned 429. Backoff try {tries} / {max_tries}.'
83                 f' We are backing off for {sleep_secs}',
84             )
85             tries += 1
86             gevent.sleep(sleep_secs)
87             continue
88 
89         if response.status_code != 200:
90             raise RemoteError(
91                 f'Beaconcha.in api request {url} failed with code: {response.status_code}'
92                 f' and response: {response.text}',
93             )
94 
95         break  # else all good - break from the loop
96 
97     log.debug('Got beaconcha.in stats results. Processing it.')
98     soup = BeautifulSoup(response.text, 'html.parser', parse_only=SoupStrainer('tbod'))
99     if soup is None:
100         raise RemoteError('Could not find <tbod> while parsing beaconcha.in stats page')
101     try:
102         tr = soup.tbod.tr
103     except AttributeError as e:
104         raise RemoteError('Could not find first <tr> while parsing beaconcha.in stats page') from e
105 
106     timestamp = Timestamp(0)
107     pnl = ZERO
108     start_amount = ZERO
109     end_amount = ZERO
110     missed_attestations = 0
111     orphaned_attestations = 0
112     proposed_blocks = 0
113     missed_blocks = 0
114     orphaned_blocks = 0
115     included_attester_slashings = 0
116     proposer_attester_slashings = 0
117     deposits_number = 0
118     amount_deposited = ZERO
119     column_pos = 1
120     stats: list[ValidatorDailyStats] = []
121     while tr is not None:
122 
123         for column in tr.children:
124             if column.name != 'td':
125                 continue
126 
127             if column_pos == 1:  # date
128                 date = column.string
129                 try:
130                     timestamp = create_timestamp(date, formatstr='%d %b %Y')
131                 except ValueError as e:
132                     raise RemoteError(f'Failed to parse {date} to timestamp') from e
133 
134                 if timestamp <= last_known_timestamp:
135                     return stats  # we are done
136 
137                 column_pos += 1
138             elif column_pos == 2:
139                 pnl = _parse_fval(column.string, 'income')
140                 column_pos += 1
141             elif column_pos == 3:
142                 start_amount = _parse_fval(column.string, 'start amount')
143                 column_pos += 1
144             elif column_pos == 4:
145                 end_amount = _parse_fval(column.string, 'end amount')
146                 column_pos += 1
147             elif column_pos == 5:
148                 missed_attestations = _parse_int(column.string, 'missed attestations')
149                 column_pos += 1
150             elif column_pos == 6:
151                 orphaned_attestations = _parse_int(column.string, 'orphaned attestations')
152                 column_pos += 1
153             elif column_pos == 7:
154                 proposed_blocks = _parse_int(column.string, 'proposed blocks')
155                 column_pos += 1
156             elif column_pos == 8:
157                 missed_blocks = _parse_int(column.string, 'missed blocks')
158                 column_pos += 1
159             elif column_pos == 9:
160                 orphaned_blocks = _parse_int(column.string, 'orphaned blocks')
161                 column_pos += 1
162             elif column_pos == 10:
163                 included_attester_slashings = _parse_int(column.string, 'included attester slashings')  # noqa: E501
164                 column_pos += 1
165             elif column_pos == 11:
166                 proposer_attester_slashings = _parse_int(column.string, 'proposer attester slashings')  # noqa: E501
167                 column_pos += 1
168             elif column_pos == 12:
169                 deposits_number = _parse_int(column.string, 'deposits number')
170                 column_pos += 1
171             elif column_pos == 13:
172                 amount_deposited = _parse_fval(column.string, 'amount deposited')
173                 column_pos += 1
174 
175         column_pos = 1
176         prices = [
177             query_usd_price_zero_if_error(
178                 A_ETH,
179                 time=time,
180                 location='eth2 staking daily stats',
181                 msg_aggregator=msg_aggregator,
182             )
183             for time in (timestamp, Timestamp(timestamp + DAY_IN_SECONDS))
184         ]
185         stats.append(ValidatorDailyStats(
186             validator_index=validator_index,
187             timestamp=timestamp,
188             start_usd_price=prices[0],
189             end_usd_price=prices[1],
190             pnl=pnl,
191             start_amount=start_amount,
192             end_amount=end_amount,
193             missed_attestations=missed_attestations,
194             orphaned_attestations=orphaned_attestations,
195             proposed_blocks=proposed_blocks,
196             missed_blocks=missed_blocks,
197             orphaned_blocks=orphaned_blocks,
198             included_attester_slashings=included_attester_slashings,
199             proposer_attester_slashings=proposer_attester_slashings,
200             deposits_number=deposits_number,
201             amount_deposited=amount_deposited,
202         ))
203         tr = tr.find_next_sibling()
204 
205     log.debug('Processed beaconcha.in stats results. Returning it.')
206     return stats
207
```
--- 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()

```

diff --git a/rotkehlchen/chain/ethereum/modules/eth2/utils.py b/rotkehlchen/chain/ethereum/modules/eth2/utils.py
--- a/rotkehlchen/chain/ethereum/modules/eth2/utils.py
+++ b/rotkehlchen/chain/ethereum/modules/eth2/utils.py
@@ -7,7 +7,7 @@
 from bs4 import BeautifulSoup, SoupStrainer
 
 from rotkehlchen.constants.assets import A_ETH
-from rotkehlchen.constants.misc import ZERO
+from rotkehlchen.constants.misc import ONE, ZERO
 from rotkehlchen.constants.timing import DAY_IN_SECONDS, DEFAULT_TIMEOUT_TUPLE
 from rotkehlchen.errors.misc import RemoteError
 from rotkehlchen.fval import FVal
@@ -22,6 +22,9 @@
 logger = logging.getLogger(__name__)
 log = RotkehlchenLogsAdapter(logger)
 
+DAY_AFTER_ETH2_GENESIS = Timestamp(1606780800)
+INITIAL_ETH_DEPOSIT = FVal(32)
+
 
 class ValidatorBalance(NamedTuple):
     epoch: int
@@ -137,6 +140,13 @@
                 column_pos += 1
             elif column_pos == 2:
                 pnl = _parse_fval(column.string, 'income')
+                # if the validator makes profit in the genesis day beaconchain returns a
+                # profit of deposit + validation reward. We need to subtract the deposit value
+                # to obtain the actual pnl.
+                # Example: https://beaconcha.in/validator/999/stats
+                if pnl > ONE and timestamp == DAY_AFTER_ETH2_GENESIS:
+                    pnl -= INITIAL_ETH_DEPOSIT
+
                 column_pos += 1
             elif column_pos == 3:
                 start_amount = _parse_fval(column.string, 'start amount')

{"golden_diff": "diff --git a/rotkehlchen/chain/ethereum/modules/eth2/utils.py b/rotkehlchen/chain/ethereum/modules/eth2/utils.py\n--- a/rotkehlchen/chain/ethereum/modules/eth2/utils.py\n+++ b/rotkehlchen/chain/ethereum/modules/eth2/utils.py\n@@ -7,7 +7,7 @@\n from bs4 import BeautifulSoup, SoupStrainer\n \n from rotkehlchen.constants.assets import A_ETH\n-from rotkehlchen.constants.misc import ZERO\n+from rotkehlchen.constants.misc import ONE, ZERO\n from rotkehlchen.constants.timing import DAY_IN_SECONDS, DEFAULT_TIMEOUT_TUPLE\n from rotkehlchen.errors.misc import RemoteError\n from rotkehlchen.fval import FVal\n@@ -22,6 +22,9 @@\n logger = logging.getLogger(__name__)\n log = RotkehlchenLogsAdapter(logger)\n \n+DAY_AFTER_ETH2_GENESIS = Timestamp(1606780800)\n+INITIAL_ETH_DEPOSIT = FVal(32)\n+\n \n class ValidatorBalance(NamedTuple):\n     epoch: int\n@@ -137,6 +140,13 @@\n                 column_pos += 1\n             elif column_pos == 2:\n                 pnl = _parse_fval(column.string, 'income')\n+                # if the validator makes profit in the genesis day beaconchain returns a\n+                # profit of deposit + validation reward. We need to subtract the deposit value\n+                # to obtain the actual pnl.\n+                # Example: https://beaconcha.in/validator/999/stats\n+                if pnl > ONE and timestamp == DAY_AFTER_ETH2_GENESIS:\n+                    pnl -= INITIAL_ETH_DEPOSIT\n+\n                 column_pos += 1\n             elif column_pos == 3:\n                 start_amount = _parse_fval(column.string, 'start amount')\n", "issue": "PnL Report is wrong for Genesis ETH2 Validator\n## Problem Definition\r\n\r\nFor a validator that deposited prior to the Beacon Chain Genesis date\r\n- the post genesis deposit event (from the 06/11/2020) is treated as if the 32 ETH were sold\r\n- the genesis day `ETH2 staking daily PnL` value (from the 01/12/2020) in the Profit and Loss Report is too large by 32 ETH (32.016 ETH instead of 0.016 ETH) \r\n\r\n![Bildschirmfoto 2023-01-13 um 00 04 16](https://user-images.githubusercontent.com/20623991/212202315-827637c4-e4ae-4ab5-92fd-4dc3b91142c4.png)\r\n\r\nOne day later, the `ETH2 staking daily PnL` value is correctly calculated.\r\n![Bildschirmfoto 2023-01-13 um 00 06 01](https://user-images.githubusercontent.com/20623991/212202331-60fe1a24-2266-4fd5-9d88-f92bd004e67f.png)\r\n\r\n## Logs\r\n\r\nI can provide logs if needed via a DM.\r\n\r\nI assume that you can reproduce for any pre-geneis deposited validator. Accordingly, I suggest that you pick a validator with a sufficiently low validator index, e.g. [Validator 999](https://beaconcha.in/validator/999) who [deposited on 05/12/2020](https://etherscan.io/tx/0x187bef85f7797f4f42534fcfa080ed28ab77491b79fe9e9be8039416eebab6bc)\r\n\r\n\r\n### System Description\r\n\r\nOperating system: MacOS Montery\r\nRotki version: 1.26.3\r\n\n", "before_files": [{"content": "import logging\nfrom http import HTTPStatus\nfrom typing import NamedTuple\n\nimport gevent\nimport requests\nfrom bs4 import BeautifulSoup, SoupStrainer\n\nfrom rotkehlchen.constants.assets import A_ETH\nfrom rotkehlchen.constants.misc import ZERO\nfrom rotkehlchen.constants.timing import DAY_IN_SECONDS, DEFAULT_TIMEOUT_TUPLE\nfrom rotkehlchen.errors.misc import RemoteError\nfrom rotkehlchen.fval import FVal\nfrom rotkehlchen.history.price import query_usd_price_zero_if_error\nfrom rotkehlchen.logging import RotkehlchenLogsAdapter\nfrom rotkehlchen.types import Timestamp\nfrom rotkehlchen.user_messages import MessagesAggregator\nfrom rotkehlchen.utils.misc import create_timestamp\n\nfrom .structures import ValidatorDailyStats\n\nlogger = logging.getLogger(__name__)\nlog = RotkehlchenLogsAdapter(logger)\n\n\nclass ValidatorBalance(NamedTuple):\n    epoch: int\n    balance: int  # in gwei\n    effective_balance: int  # in wei\n\n\ndef _parse_fval(line: str, entry: str) -> FVal:\n    try:\n        result = FVal(line.replace('ETH', ''))\n    except ValueError as e:\n        raise RemoteError(f'Could not parse {line} as a number for {entry}') from e\n\n    return result\n\n\ndef _parse_int(line: str, entry: str) -> int:\n    try:\n        if line == '-':\n            result = 0\n        else:\n            result = int(line)\n    except ValueError as e:\n        raise RemoteError(f'Could not parse {line} as an integer for {entry}') from e\n\n    return result\n\n\ndef scrape_validator_daily_stats(\n        validator_index: int,\n        last_known_timestamp: Timestamp,\n        msg_aggregator: MessagesAggregator,\n) -> list[ValidatorDailyStats]:\n    \"\"\"Scrapes the website of beaconcha.in and parses the data directly out of the data table.\n\n    The parser is very simple. And can break if they change stuff in the way\n    it's displayed in https://beaconcha.in/validator/33710/stats. If that happpens\n    we need to adjust here. If we also somehow programatically get the data in a CSV\n    that would be swell.\n\n    May raise:\n    - RemoteError if we can't query beaconcha.in or if the data is not in the expected format\n    \"\"\"\n    url = f'https://beaconcha.in/validator/{validator_index}/stats'\n    tries = 1\n    max_tries = 3\n    backoff = 60\n    while True:\n        log.debug(f'Querying beaconcha.in stats: {url}')\n        try:\n            response = requests.get(url, timeout=DEFAULT_TIMEOUT_TUPLE)\n        except requests.exceptions.RequestException as e:\n            raise RemoteError(f'Beaconcha.in api request {url} failed due to {str(e)}') from e\n\n        if response.status_code == HTTPStatus.TOO_MANY_REQUESTS and tries <= max_tries:\n            sleep_secs = backoff * tries / max_tries\n            log.warning(\n                f'Querying {url} returned 429. Backoff try {tries} / {max_tries}.'\n                f' We are backing off for {sleep_secs}',\n            )\n            tries += 1\n            gevent.sleep(sleep_secs)\n            continue\n\n        if response.status_code != 200:\n            raise RemoteError(\n                f'Beaconcha.in api request {url} failed with code: {response.status_code}'\n                f' and response: {response.text}',\n            )\n\n        break  # else all good - break from the loop\n\n    log.debug('Got beaconcha.in stats results. Processing it.')\n    soup = BeautifulSoup(response.text, 'html.parser', parse_only=SoupStrainer('tbod'))\n    if soup is None:\n        raise RemoteError('Could not find <tbod> while parsing beaconcha.in stats page')\n    try:\n        tr = soup.tbod.tr\n    except AttributeError as e:\n        raise RemoteError('Could not find first <tr> while parsing beaconcha.in stats page') from e\n\n    timestamp = Timestamp(0)\n    pnl = ZERO\n    start_amount = ZERO\n    end_amount = ZERO\n    missed_attestations = 0\n    orphaned_attestations = 0\n    proposed_blocks = 0\n    missed_blocks = 0\n    orphaned_blocks = 0\n    included_attester_slashings = 0\n    proposer_attester_slashings = 0\n    deposits_number = 0\n    amount_deposited = ZERO\n    column_pos = 1\n    stats: list[ValidatorDailyStats] = []\n    while tr is not None:\n\n        for column in tr.children:\n            if column.name != 'td':\n                continue\n\n            if column_pos == 1:  # date\n                date = column.string\n                try:\n                    timestamp = create_timestamp(date, formatstr='%d %b %Y')\n                except ValueError as e:\n                    raise RemoteError(f'Failed to parse {date} to timestamp') from e\n\n                if timestamp <= last_known_timestamp:\n                    return stats  # we are done\n\n                column_pos += 1\n            elif column_pos == 2:\n                pnl = _parse_fval(column.string, 'income')\n                column_pos += 1\n            elif column_pos == 3:\n                start_amount = _parse_fval(column.string, 'start amount')\n                column_pos += 1\n            elif column_pos == 4:\n                end_amount = _parse_fval(column.string, 'end amount')\n                column_pos += 1\n            elif column_pos == 5:\n                missed_attestations = _parse_int(column.string, 'missed attestations')\n                column_pos += 1\n            elif column_pos == 6:\n                orphaned_attestations = _parse_int(column.string, 'orphaned attestations')\n                column_pos += 1\n            elif column_pos == 7:\n                proposed_blocks = _parse_int(column.string, 'proposed blocks')\n                column_pos += 1\n            elif column_pos == 8:\n                missed_blocks = _parse_int(column.string, 'missed blocks')\n                column_pos += 1\n            elif column_pos == 9:\n                orphaned_blocks = _parse_int(column.string, 'orphaned blocks')\n                column_pos += 1\n            elif column_pos == 10:\n                included_attester_slashings = _parse_int(column.string, 'included attester slashings')  # noqa: E501\n                column_pos += 1\n            elif column_pos == 11:\n                proposer_attester_slashings = _parse_int(column.string, 'proposer attester slashings')  # noqa: E501\n                column_pos += 1\n            elif column_pos == 12:\n                deposits_number = _parse_int(column.string, 'deposits number')\n                column_pos += 1\n            elif column_pos == 13:\n                amount_deposited = _parse_fval(column.string, 'amount deposited')\n                column_pos += 1\n\n        column_pos = 1\n        prices = [\n            query_usd_price_zero_if_error(\n                A_ETH,\n                time=time,\n                location='eth2 staking daily stats',\n                msg_aggregator=msg_aggregator,\n            )\n            for time in (timestamp, Timestamp(timestamp + DAY_IN_SECONDS))\n        ]\n        stats.append(ValidatorDailyStats(\n            validator_index=validator_index,\n            timestamp=timestamp,\n            start_usd_price=prices[0],\n            end_usd_price=prices[1],\n            pnl=pnl,\n            start_amount=start_amount,\n            end_amount=end_amount,\n            missed_attestations=missed_attestations,\n            orphaned_attestations=orphaned_attestations,\n            proposed_blocks=proposed_blocks,\n            missed_blocks=missed_blocks,\n            orphaned_blocks=orphaned_blocks,\n            included_attester_slashings=included_attester_slashings,\n            proposer_attester_slashings=proposer_attester_slashings,\n            deposits_number=deposits_number,\n            amount_deposited=amount_deposited,\n        ))\n        tr = tr.find_next_sibling()\n\n    log.debug('Processed beaconcha.in stats results. Returning it.')\n    return stats\n", "path": "rotkehlchen/chain/ethereum/modules/eth2/utils.py"}], "after_files": [{"content": "import logging\nfrom http import HTTPStatus\nfrom typing import NamedTuple\n\nimport gevent\nimport requests\nfrom bs4 import BeautifulSoup, SoupStrainer\n\nfrom rotkehlchen.constants.assets import A_ETH\nfrom rotkehlchen.constants.misc import ONE, ZERO\nfrom rotkehlchen.constants.timing import DAY_IN_SECONDS, DEFAULT_TIMEOUT_TUPLE\nfrom rotkehlchen.errors.misc import RemoteError\nfrom rotkehlchen.fval import FVal\nfrom rotkehlchen.history.price import query_usd_price_zero_if_error\nfrom rotkehlchen.logging import RotkehlchenLogsAdapter\nfrom rotkehlchen.types import Timestamp\nfrom rotkehlchen.user_messages import MessagesAggregator\nfrom rotkehlchen.utils.misc import create_timestamp\n\nfrom .structures import ValidatorDailyStats\n\nlogger = logging.getLogger(__name__)\nlog = RotkehlchenLogsAdapter(logger)\n\nDAY_AFTER_ETH2_GENESIS = Timestamp(1606780800)\nINITIAL_ETH_DEPOSIT = FVal(32)\n\n\nclass ValidatorBalance(NamedTuple):\n    epoch: int\n    balance: int  # in gwei\n    effective_balance: int  # in wei\n\n\ndef _parse_fval(line: str, entry: str) -> FVal:\n    try:\n        result = FVal(line.replace('ETH', ''))\n    except ValueError as e:\n        raise RemoteError(f'Could not parse {line} as a number for {entry}') from e\n\n    return result\n\n\ndef _parse_int(line: str, entry: str) -> int:\n    try:\n        if line == '-':\n            result = 0\n        else:\n            result = int(line)\n    except ValueError as e:\n        raise RemoteError(f'Could not parse {line} as an integer for {entry}') from e\n\n    return result\n\n\ndef scrape_validator_daily_stats(\n        validator_index: int,\n        last_known_timestamp: Timestamp,\n        msg_aggregator: MessagesAggregator,\n) -> list[ValidatorDailyStats]:\n    \"\"\"Scrapes the website of beaconcha.in and parses the data directly out of the data table.\n\n    The parser is very simple. And can break if they change stuff in the way\n    it's displayed in https://beaconcha.in/validator/33710/stats. If that happpens\n    we need to adjust here. If we also somehow programatically get the data in a CSV\n    that would be swell.\n\n    May raise:\n    - RemoteError if we can't query beaconcha.in or if the data is not in the expected format\n    \"\"\"\n    url = f'https://beaconcha.in/validator/{validator_index}/stats'\n    tries = 1\n    max_tries = 3\n    backoff = 60\n    while True:\n        log.debug(f'Querying beaconcha.in stats: {url}')\n        try:\n            response = requests.get(url, timeout=DEFAULT_TIMEOUT_TUPLE)\n        except requests.exceptions.RequestException as e:\n            raise RemoteError(f'Beaconcha.in api request {url} failed due to {str(e)}') from e\n\n        if response.status_code == HTTPStatus.TOO_MANY_REQUESTS and tries <= max_tries:\n            sleep_secs = backoff * tries / max_tries\n            log.warning(\n                f'Querying {url} returned 429. Backoff try {tries} / {max_tries}.'\n                f' We are backing off for {sleep_secs}',\n            )\n            tries += 1\n            gevent.sleep(sleep_secs)\n            continue\n\n        if response.status_code != 200:\n            raise RemoteError(\n                f'Beaconcha.in api request {url} failed with code: {response.status_code}'\n                f' and response: {response.text}',\n            )\n\n        break  # else all good - break from the loop\n\n    log.debug('Got beaconcha.in stats results. Processing it.')\n    soup = BeautifulSoup(response.text, 'html.parser', parse_only=SoupStrainer('tbod'))\n    if soup is None:\n        raise RemoteError('Could not find <tbod> while parsing beaconcha.in stats page')\n    try:\n        tr = soup.tbod.tr\n    except AttributeError as e:\n        raise RemoteError('Could not find first <tr> while parsing beaconcha.in stats page') from e\n\n    timestamp = Timestamp(0)\n    pnl = ZERO\n    start_amount = ZERO\n    end_amount = ZERO\n    missed_attestations = 0\n    orphaned_attestations = 0\n    proposed_blocks = 0\n    missed_blocks = 0\n    orphaned_blocks = 0\n    included_attester_slashings = 0\n    proposer_attester_slashings = 0\n    deposits_number = 0\n    amount_deposited = ZERO\n    column_pos = 1\n    stats: list[ValidatorDailyStats] = []\n    while tr is not None:\n\n        for column in tr.children:\n            if column.name != 'td':\n                continue\n\n            if column_pos == 1:  # date\n                date = column.string\n                try:\n                    timestamp = create_timestamp(date, formatstr='%d %b %Y')\n                except ValueError as e:\n                    raise RemoteError(f'Failed to parse {date} to timestamp') from e\n\n                if timestamp <= last_known_timestamp:\n                    return stats  # we are done\n\n                column_pos += 1\n            elif column_pos == 2:\n                pnl = _parse_fval(column.string, 'income')\n                # if the validator makes profit in the genesis day beaconchain returns a\n                # profit of deposit + validation reward. We need to subtract the deposit value\n                # to obtain the actual pnl.\n                # Example: https://beaconcha.in/validator/999/stats\n                if pnl > ONE and timestamp == DAY_AFTER_ETH2_GENESIS:\n                    pnl -= INITIAL_ETH_DEPOSIT\n\n                column_pos += 1\n            elif column_pos == 3:\n                start_amount = _parse_fval(column.string, 'start amount')\n                column_pos += 1\n            elif column_pos == 4:\n                end_amount = _parse_fval(column.string, 'end amount')\n                column_pos += 1\n            elif column_pos == 5:\n                missed_attestations = _parse_int(column.string, 'missed attestations')\n                column_pos += 1\n            elif column_pos == 6:\n                orphaned_attestations = _parse_int(column.string, 'orphaned attestations')\n                column_pos += 1\n            elif column_pos == 7:\n                proposed_blocks = _parse_int(column.string, 'proposed blocks')\n                column_pos += 1\n            elif column_pos == 8:\n                missed_blocks = _parse_int(column.string, 'missed blocks')\n                column_pos += 1\n            elif column_pos == 9:\n                orphaned_blocks = _parse_int(column.string, 'orphaned blocks')\n                column_pos += 1\n            elif column_pos == 10:\n                included_attester_slashings = _parse_int(column.string, 'included attester slashings')  # noqa: E501\n                column_pos += 1\n            elif column_pos == 11:\n                proposer_attester_slashings = _parse_int(column.string, 'proposer attester slashings')  # noqa: E501\n                column_pos += 1\n            elif column_pos == 12:\n                deposits_number = _parse_int(column.string, 'deposits number')\n                column_pos += 1\n            elif column_pos == 13:\n                amount_deposited = _parse_fval(column.string, 'amount deposited')\n                column_pos += 1\n\n        column_pos = 1\n        prices = [\n            query_usd_price_zero_if_error(\n                A_ETH,\n                time=time,\n                location='eth2 staking daily stats',\n                msg_aggregator=msg_aggregator,\n            )\n            for time in (timestamp, Timestamp(timestamp + DAY_IN_SECONDS))\n        ]\n        stats.append(ValidatorDailyStats(\n            validator_index=validator_index,\n            timestamp=timestamp,\n            start_usd_price=prices[0],\n            end_usd_price=prices[1],\n            pnl=pnl,\n            start_amount=start_amount,\n            end_amount=end_amount,\n            missed_attestations=missed_attestations,\n            orphaned_attestations=orphaned_attestations,\n            proposed_blocks=proposed_blocks,\n            missed_blocks=missed_blocks,\n            orphaned_blocks=orphaned_blocks,\n            included_attester_slashings=included_attester_slashings,\n            proposer_attester_slashings=proposer_attester_slashings,\n            deposits_number=deposits_number,\n            amount_deposited=amount_deposited,\n        ))\n        tr = tr.find_next_sibling()\n\n    log.debug('Processed beaconcha.in stats results. Returning it.')\n    return stats\n", "path": "rotkehlchen/chain/ethereum/modules/eth2/utils.py"}]}