Repository: rotki/rotki

Issue:
Incorrect Yearn v2 USDC price
## Problem Definition
The Yearn v2 USDC vault shows an incorrect price of 1. This is the result of the issue noted by the devs here: https://github.com/rotki/rotki/blob/975f97deea470dd5f659f9928373ee134bfe54a4/rotkehlchen/chain/ethereum/defi/price.py#L144

## Potential Solution
In rotkehlchen/chain/ethereum/defi/price.py -> `handle_underlying_price_yearn_vault`

Instead of:
```python
    # or understand why yUSDC and yUSDT which have 6 decimals don't work correctly
    div_decimals = 18
    price_per_full_share = contract.call(ethereum, 'getPricePerFullShare')
    usd_value = FVal(asset_price * price_per_full_share) / 10 ** div_decimals
    return usd_value
```

Try:
```python
    # see https://github.com/BobTheBuidler/ypricemagic/blob/882aa2071a918937e77e0b85e5f52191a4714d28/ypricemagic/yearn.py#L13
    # Yearn v1 vaults use getPricePerFullShare scaled to 18 decimals
    # Yearn v2 vaults use pricePerShare scaled to underlying token decimals

    if hasattr(contract, "pricePerShare"):
        div_decimals = contract.call(ethereum, "decimals")
        price_per_share = contract.call(ethereum, "pricePerShare")
        usd_value = FVal(asset_price * price_per_share) / 10 ** div_decimals
        return usd_value

    if hasattr(contract, "getPricePerFullShare"):
        div_decimals = 18
        price_per_full_share = contract.call(ethereum, "getPricePerFullShare")
        usd_value = FVal(asset_price * price_per_full_share) / 10 ** div_decimals
        return usd_value

    if hasattr(contract, "getPricePerShare"):
        div_decimals = contract.call(ethereum, "decimals")
        price_per_share = contract.call(ethereum, "getPricePerShare")
        usd_value = FVal(asset_price * price_per_share) / 10 ** div_decimals
        return usd_value
```

### System Description


Operating system: macOS Monterey
Rotki version: 1.20.0



Discussion:
@e-gons Thank you for explaining the issue with such detail citing the yearn code. Will review your suggestions and fix the issue hopefully in the next pach release :)
Hello @e-gons I've been reading the code and in fact is the same we do. The difference is that we have it split for yearn v1 and v2 since the use is different. We have into account this details, the only thing I couldn't find is what contracts in production use `getPricePerShare` as all of them seem to use either `getPricePerFullShare` or `pricePerShare`

Resolving patch:
diff --git a/rotkehlchen/chain/ethereum/defi/price.py b/rotkehlchen/chain/ethereum/defi/price.py
--- a/rotkehlchen/chain/ethereum/defi/price.py
+++ b/rotkehlchen/chain/ethereum/defi/price.py
@@ -141,8 +141,6 @@ def handle_underlying_price_yearn_vault(
         div_decimals: int,
         asset_price: Price,
 ) -> FVal:
-    # TODO This needs to change. Either make it constant for all vaults of this type
-    # or understand why yUSDC and yUSDT which have 6 decimals don't work correctly
     div_decimals = 18
     price_per_full_share = contract.call(ethereum, 'getPricePerFullShare')
     usd_value = FVal(asset_price * price_per_full_share) / 10 ** div_decimals
diff --git a/rotkehlchen/inquirer.py b/rotkehlchen/inquirer.py
--- a/rotkehlchen/inquirer.py
+++ b/rotkehlchen/inquirer.py
@@ -57,6 +57,7 @@
 from rotkehlchen.constants.ethereum import CURVE_POOL_ABI, UNISWAP_V2_LP_ABI, YEARN_VAULT_V2_ABI
 from rotkehlchen.constants.timing import DAY_IN_SECONDS, MONTH_IN_SECONDS
 from rotkehlchen.errors import (
+    BlockchainQueryError,
     DeserializationError,
     PriceQueryUnsupportedAsset,
     RemoteError,
@@ -188,6 +189,12 @@ def get_underlying_asset_price(token: EthereumToken) -> Optional[Price]:
     elif token in ASSETS_UNDERLYING_BTC:
         price = Inquirer().find_usd_price(A_BTC)
 
+    # At this point we have to return the price if it's not None. If we don't do this and got
+    # a price for a token that has underlying assets, the code will enter the if statement after
+    # this block and the value for price will change becoming incorrect.
+    if price is not None:
+        return price
+
     custom_token = GlobalDBHandler().get_ethereum_token(token.ethereum_address)
     if custom_token and custom_token.underlying_tokens is not None:
         usd_price = ZERO
@@ -665,21 +672,12 @@ def find_yearn_price(
             abi=YEARN_VAULT_V2_ABI,
             deployed_block=0,
         )
-        output = multicall_2(
-            ethereum=self._ethereum,
-            require_success=True,
-            calls=[(token.ethereum_address, contract.encode(method_name='pricePerShare'))],
-        )
-        if isinstance(output, list) and len(output) != 0 and output[0][0] is True:
-            price_per_share = contract.decode(output[0][1], 'pricePerShare')
-            if len(price_per_share) != 0:
-                return price_per_share[0] * underlying_token_price
-            log.error(f'Failed to decode pricePerShare for yearn vault v2 token {token}')
-            # will return None right below
-        log.error(
-            f'Error to call multicall for price on Yearn vault v2 token {token} '
-            f'with output {output}.',
-        )
+        try:
+            price_per_share = contract.call(self._ethereum, 'pricePerShare')
+            return Price(price_per_share * underlying_token_price / 10 ** token.decimals)
+        except (RemoteError, BlockchainQueryError) as e:
+            log.error(f'Failed to query pricePerShare method in Yearn v2 Vault. {str(e)}')
+
         return None
 
     @staticmethod


Test patch: