Repository: rotki/rotki

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



Discussion:

Resolving patch:
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 @@ def scrape_validator_daily_stats(
                 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')


Test patch:
diff --git a/rotkehlchen/tests/data/mocks/test_eth2/validator_daily_stats/999.html b/rotkehlchen/tests/data/mocks/test_eth2/validator_daily_stats/999.html
new file mode 100644
--- /dev/null
+++ b/rotkehlchen/tests/data/mocks/test_eth2/validator_daily_stats/999.html
@@ -0,0 +1,175 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+  </head>
+  <header></header>
+  <body ontouchstart="">
+    
+    <div  class="info-banner-container">
+      <div class="info-banner-content container">
+        <div class="info-banner-right">
+            <div class="d-md-none">
+              <div class="dropdown" class="d-md-none">
+                <a class="btn btn-transparent btn-sm dropdown-toggle" id="loginDropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                  <i style="margin: 0; padding: 0;" class="fas fa-sign-in-alt"></i>
+                </a>
+                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="loginDropdown">
+                  <a class="dropdown-item" href="/login">Log in</a>
+                  <a class="dropdown-item" href="/register">Sign Up</a>
+                </div>
+              </div>
+            </div>
+            <a href="/login" class="mr-3 d-none d-md-flex"><span>Log in</span></a>
+            <a href="/register" class="btn btn-primary btn-sm d-none d-md-flex"
+              ><span class="text-white"><b>Sign Up</b></span></a
+            >
+          
+        </div>
+      </div>
+    </div>
+    
+
+    <nav  id="nav" class="navbar navbar-expand-lg navbar-light">
+      <div class="container">
+        <a class="navbar-brand" href="/">
+          <svg style="width: 22px; height: 22px; margin-bottom: .55rem;" id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
+            <defs>
+              <style>
+                .cls-1 {
+                  fill: #1d1d1b;
+                }
+
+                .cls-2 {
+                  fill: #fff;
+                }
+                [data-theme="dark"] .cls-1 {
+                  fill: #fff;
+                }
+
+                [data-theme="dark"] .cls-2 {
+                  fill: #1e1c1f;
+                }
+              </style>
+            </defs>
+            <rect class="cls-1" x="-14.88" y="-19.92" width="149.75" height="146.77" />
+            <path
+              class="cls-2"
+              d="M59-12.9c-47.25,0-66.43-4.44-66.43,32.35C-7.45,45-8.17,120-8.17,120H22.56S35.78,94.52,36.41,93.86c1.36-1.42,20.81,9,22,10.23.25.26,5.4,15.91,5.4,15.91h64.48s-.15-68-.15-90.77C128.17-15.07,99.59-12.9,59-12.9ZM84.32,88c-16.59,13.26-43.13,6.9-51-15.71a36.18,36.18,0,0,1-1.21-19.85c.85-4,2.2-4.57,5.49-2.37,7,4.68,13.88,9.24,21.36,14.22.73-1.33,1.7-2.6,2.35-4.05a4.31,4.31,0,0,0,.31-2.3c-.5-3.11,1-5.85,3.83-6.55a5.18,5.18,0,0,1,6.24,4,5.48,5.48,0,0,1-3.93,6.44,2.48,2.48,0,0,0-1.24.65c-.17.26-3.07,4.92-3.07,4.92L73.72,74.5c3.47,2.39,6.91,4.76,10.41,7.1S87.52,85.42,84.32,88ZM66.2,39.23a16.16,16.16,0,0,1,16.27,9.91c.06.15.12.31.19.46.66,1.53.42,2-1.22,2.44l-.59.15c-1.08.28-1.25-.7-2.07-2.57a11.47,11.47,0,0,0-11.87-6.85c-1.65.18-2.79.48-3-.39C63.25,39.65,63.37,39.49,66.2,39.23ZM89.32,50l-.92.24c-1.68.44-2-1.13-3.25-4.13A18,18,0,0,0,66.62,35c-2.55.26-4.32.72-4.66-.67-1.05-4.37-.87-4.62,3.53-5a25.51,25.51,0,0,1,25.4,16c.09.25.18.5.29.75C92.22,48.55,91.86,49.3,89.32,50Zm11.77-3.08-1.4.36-.77.2c-2.19.57-2.68.21-3.62-1.91a42.4,42.4,0,0,0-3.91-7.76c-7.3-10-17-14.34-29.28-12.41-1.47.24-2.36-.22-2.72-1.71-.22-1-.47-1.9-.68-2.86-.38-1.72.17-2.58,1.86-2.89,10.6-2,20.2.43,28.84,6.86,6.46,4.81,10.69,11.29,13.32,19C103.34,45.49,102.85,46.39,101.09,46.89Z" />
+          </svg>
+          <span class="brand-text">beaconcha.in</span>
+        </a>
+        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
+          <span class="navbar-toggler-icon"></span>
+        </button>
+        <div class="collapse navbar-collapse" id="navbarSupportedContent">
+        </div>
+      </div>
+    </nav>
+    <main>
+
+  <div class="container mt-2 validator-content">
+    <div class="my-3 py-2">
+      <div class="d-md-flex justify-content-md-between">
+        <div class="d-flex mb-1">
+          <h1 class="h4 mb-1 mb-md-0">
+            <span>Daily Statistics for Validator 999</span>
+          </h1>
+        </div>
+        <nav aria-label="breadcrumb">
+          <ol class="breadcrumb font-size-1 mb-0" style="padding:0; background-color:transparent;">
+            <li class="breadcrumb-item"><a href="/" title="Home">Home</a></li>
+            <li class="breadcrumb-item"><a href="/validators" title="Validators">Validators</a></li>
+            <li class="breadcrumb-item"><a href="/validator/999" title="Validators">Validator details</a></li>
+            <li class="breadcrumb-item active" aria-current="page">Daily Stats</li>
+          </ol>
+        </nav>
+      </div>
+    </div>
+    <table class="table table-sm table-striped" id="stats-table">
+      <thead>
+        <tr>
+          <th>Day</th>
+          <th colspan="3">Balance</th>
+          <th colspan="2">Attestations</th>
+          <th colspan="3">Blocks</th>
+          <th colspan="2">Slashings</th>
+          <th colspan="2">Deposits</th>
+        </tr>
+        <tr>
+          <th></th>
+          <th title="Income of the day" data-toggle="tooltip">Income</th>
+          <th title="Balance at the start of the day" data-toggle="tooltip">Start</th>
+          <th title="Balance at the end of the day" data-toggle="tooltip">End</th>
+          <th title="Amount of missed attestations" data-toggle="tooltip">M</th>
+          <th title="Amount of orphaned attestations" data-toggle="tooltip">O</th>
+          <th title="Amount of proposed blocks" data-toggle="tooltip">P</th>
+          <th title="Amount of missed blocks" data-toggle="tooltip">M</th>
+          <th title="Amount of orphaned blocks" data-toggle="tooltip">O</th>
+          <th title="Amount of included attester slashings" data-toggle="tooltip">Att</th>
+          <th title="Amount of proposer attester slashings" data-toggle="tooltip">Prop</th>
+          <th title="Amount of deposits" data-toggle="tooltip">#</th>
+          <th title="ETH deposited" data-toggle="tooltip">Amount</th>
+        </tr>
+      </thead>
+      <tbod>
+          <tr>
+            <td data-order="0">01 Dec 2020</td>
+            <td><span class="text-success"><b>+32.01201 ETH</b></span></td>
+            <td>0 ETH</td>
+            <td>32.01201 ETH</td>
+            <td>2</td>
+            <td>0</td>
+            <td>0</td>
+            <td>0</td>
+            <td>0</td>
+            <td>0</td>
+            <td>0</td>
+            <td>0</td>
+            <td><b>0 ETH</b></td>
+          </tr>
+        
+          <tr>
+            <td data-order="-1">30 Nov 2020</td>
+            <td><b>0 ETH</b></td>
+            <td>0 ETH</td>
+            <td>0 ETH</td>
+            <td>0</td>
+            <td>0</td>
+            <td>0</td>
+            <td>0</td>
+            <td>0</td>
+            <td>0</td>
+            <td>0</td>
+            <td>1</td>
+            <td><span class="text-success"><b>+32 ETH</b></span></td>
+          </tr>
+        
+      </tbod>
+    </table>
+  </div>
+
+
+      
+        <div id="cookie-banner" class="d-none" style="position: fixed; display: flex; flex-direction: column; z-index: 999; bottom: 3%; left: 50%; transform: translateX(-50%); width: 100%; justify-content: center; align-items: center;">
+          <div style="width: 80%; max-width: 600px;" class="card card-body row">
+            <div class="row">
+              <div class="col">
+                <span>By using our site you agree to our <a href="/legal/privacy.pdf">use of cookies</a> to deliver a better user experience.</span>
+              </div>
+            </div>
+            <div class="row">
+              <div class="col d-flex justify-content-end align-items-end">
+                <button class="btn btn-secondary btn" style="margin-left: 1%;" onclick="acceptOnlyNecessaryCookies()">Only Necessary</button>
+                <button class="btn btn-primary btn" style="margin-left: 1%;" onclick="acceptCookieBanner()">Accept All Cookies</button>
+                <div></div>
+              </div>
+            </div>
+          </div>
+        </div>
+      
+    </main>
+    <div style="margin-top: 3rem; margin-bottom: 4.2rem;">
+      <hr />
+    </div>
+</body>
+</html>
diff --git a/rotkehlchen/tests/unit/test_eth2.py b/rotkehlchen/tests/unit/test_eth2.py
--- a/rotkehlchen/tests/unit/test_eth2.py
+++ b/rotkehlchen/tests/unit/test_eth2.py
@@ -2,6 +2,7 @@
 import re
 from contextlib import ExitStack
 from pathlib import Path
+from typing import TYPE_CHECKING
 from unittest.mock import patch
 
 import pytest
@@ -27,6 +28,10 @@
 from rotkehlchen.types import Timestamp, deserialize_evm_tx_hash
 from rotkehlchen.utils.misc import ts_now
 
+if TYPE_CHECKING:
+    from rotkehlchen.history.price import PriceHistorian
+    from rotkehlchen.user_messages import MessagesAggregator
+
 ADDR1 = string_to_evm_address('0xfeF0E7635281eF8E3B705e9C5B86e1d3B0eAb397')
 ADDR2 = string_to_evm_address('0x00F8a0D8EE1c21151BCcB416bCa1C152f9952D19')
 ADDR3 = string_to_evm_address('0x3266F3546a1e5Dc6A15588f3324741A0E20a3B6c')
@@ -888,3 +893,47 @@ def test_validator_daily_stats_with_db_interaction(  # pylint: disable=unused-ar
             )
         last_stat = stats[:len(expected_stats)][-1]
         assert last_stat.pnl_balance.amount == expected_stats[-1].pnl_balance.amount * FVal(0.45)  # noqa: E501
+
+
+@pytest.mark.parametrize('default_mock_price_value', [FVal(1.55)])
+def test_validator_daily_stats_with_genesis_event(
+        network_mocking: bool,
+        price_historian: 'PriceHistorian',  # pylint: disable=unused-argument
+        function_scope_messages_aggregator: 'MessagesAggregator',
+) -> None:
+    """
+    Test that if profit returned by beaconchain on genesis is > 32 ETH the app
+    handles it correctly.
+    """
+    validator_index = 999
+
+    with mock_scrape_validator_daily_stats(network_mocking):
+        stats = scrape_validator_daily_stats(
+            validator_index=validator_index,
+            last_known_timestamp=Timestamp(0),
+            msg_aggregator=function_scope_messages_aggregator,
+        )
+    assert stats == [
+        ValidatorDailyStats(
+            validator_index=999,
+            timestamp=Timestamp(1606780800),
+            start_amount=FVal('0'),
+            end_amount=FVal('32.01201'),
+            pnl=FVal('0.01201'),
+            start_usd_price=FVal(1.55),
+            end_usd_price=FVal(1.55),
+            missed_attestations=2,
+            ownership_percentage=ONE,
+        ), ValidatorDailyStats(
+            validator_index=999,
+            timestamp=Timestamp(1606694400),
+            start_amount=ZERO,
+            end_amount=ZERO,
+            pnl=ZERO,
+            amount_deposited=FVal(32),
+            deposits_number=1,
+            start_usd_price=FVal(1.55),
+            end_usd_price=FVal(1.55),
+            ownership_percentage=ONE,
+        ),
+    ]