Skip to content

Commit

Permalink
Adding read back to Lagoon NAV
Browse files Browse the repository at this point in the history
  • Loading branch information
miohtama committed Dec 3, 2024
1 parent f6750d4 commit 7fc01c2
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 9 deletions.
30 changes: 28 additions & 2 deletions eth_defi/lagoon/vault.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Vault adapter for Lagoon protocol."""

import logging
from dataclasses import asdict
from decimal import Decimal
from functools import cached_property
Expand All @@ -19,6 +20,9 @@
from ..token import TokenDetails, fetch_erc20_details


logger = logging.getLogger(__name__)


class LagoonVaultInfo(VaultInfo):
"""TODO: Add Lagoon vault info query"""

Expand Down Expand Up @@ -101,6 +105,15 @@ def vault_contract(self) -> Contract:
self.spec.vault_address,
)

@cached_property
def vault_contract(self) -> Contract:
"""Get vault deployment."""
return get_deployed_contract(
self.web3,
"lagoon/Vault.json",
self.spec.vault_address,
)

def fetch_vault_info(self) -> dict:
"""Get all information we can extract from the vault smart contracts."""
vault = self.vault_contract
Expand Down Expand Up @@ -222,19 +235,32 @@ def transact_through_module(
)
return bound_func

def post_valuation_commitee(
def post_new_valuation(
self,
total_valuation: Decimal,
):
) -> ContractFunction:
"""Update the valuations of this vault.
- Lagoon vault does not currently track individual positions, but takes a "total value" number
- Updating this number also allows deposits and redemptions to proceed
Notes:
> How can I post a valuation commitee update 1. as the valuationManager, call the function updateNewTotalAssets(_newTotalAssets) _newTotalAssets being expressed in underlying in its smallest unit for usdc, it would with its 6 decimals. Do not take into account requestDeposit and requestRedeem in your valuation
> 2. as the safe, call the function settleDeposit()
:param total_valuation:
The vault value nominated in :py:meth:`denomination_token`.
:return:
Bound contract function that can be turned to a transaction
"""
logger.info("Updating vault %s valuation to %s %s", self.address, total_valuation, self.denomination_token.symbol)
raw_amount = self.denomination_token.convert_to_raw(total_valuation)
bound_func = self.vault_contract.functions.updateNewTotalAssets(raw_amount)
return bound_func



Expand Down
13 changes: 13 additions & 0 deletions eth_defi/vault/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,14 @@ class VaultBase(ABC):
- [ ] read vault investors
- [ ] read vault share price
- [ ] read vault share token
- [ ] read all positions
- [ ] read NAV
- [ ] deposit integration test
- [ ] redemption integration
- [ ] swap integration test
- [ ] re-valuation integration test
- [ ] only asset manager allowed to swap negative test
- [ ] only valuation commitee allowed to update vault valuations (if applicable)
"""

@property
Expand Down Expand Up @@ -225,6 +229,15 @@ def get_flow_manager(self) -> VaultFlowManager:
def fetch_denomination_token(self) -> TokenDetails:
"""Use :py:method:`denomination_token` to access"""

@abstractmethod
def fetch_nav(self) -> Decimal:
"""Fetch the most recent onchain NAV value.
:return:
Vault NAV, denominated in :py:meth:`denomination_token`
"""


@cached_property
def denomination_token(self) -> TokenDetails:
return self.fetch_denomination_token()
Expand Down
23 changes: 21 additions & 2 deletions tests/lagoon/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,21 @@ def usdc_holder() -> HexAddress:


@pytest.fixture()
def anvil_base_fork(request, vault_owner, usdc_holder, asset_manager) -> AnvilLaunch:
def valuation_manager() -> HexAddress:
"""Unlockable account set as the vault valuation manager."""
return "0x8358bBFb4Afc9B1eBe4e8C93Db8bF0586BD8331a"


@pytest.fixture()
def anvil_base_fork(request, vault_owner, usdc_holder, asset_manager, valuation_manager) -> AnvilLaunch:
"""Create a testable fork of live BNB chain.
:return: JSON-RPC URL for Web3
"""
assert JSON_RPC_BASE, "JSON_RPC_BASE not set"
launch = fork_network_anvil(
JSON_RPC_BASE,
unlocked_addresses=[vault_owner, usdc_holder, asset_manager],
unlocked_addresses=[vault_owner, usdc_holder, asset_manager, valuation_manager],
)
try:
yield launch
Expand Down Expand Up @@ -164,6 +170,19 @@ def topped_up_asset_manager(web3, asset_manager):
return asset_manager



@pytest.fixture()
def topped_up_valuation_manager(web3, valuation_manager):
# Topped up with some ETH
tx_hash = web3.eth.send_transaction({
"to": valuation_manager,
"from": web3.eth.accounts[0],
"value": 9 * 10**18,
})
assert_transaction_success_with_explanation(web3, tx_hash)
return valuation_manager


# Some addresses for the roles set:
"""
Expand Down
4 changes: 2 additions & 2 deletions tests/lagoon/test_lagoon_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ def read_only_vault(lagoon_vault) -> LagoonVault:
return lagoon_vault


def test_lagoon_info(read_only_vault: LagoonVault):
def test_lagoon_core_info(read_only_vault: LagoonVault):
"""Get core info of Lagoon vault"""
vault = read_only_vault
info = vault.fetch_info()
assert info["address"].lower() == "0xab4ac28d10a4bc279ad073b1d74bfa0e385c010c"
assert info["safe"] == "0x20415f3Ec0FEA974548184bdD6e67575D128953F"
assert info["valuationManager"] == "0x20415f3Ec0FEA974548184bdD6e67575D128953F" # Hotkey, unlocked for tests
assert info["valuationManager"] == "0x8358bBFb4Afc9B1eBe4e8C93Db8bF0586BD8331a" # Hotkey, unlocked for tests
assert len(info["owners"]) == 2


Expand Down
21 changes: 18 additions & 3 deletions tests/lagoon/test_lagoon_valuation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
from decimal import Decimal

import pytest
from multicall import Multicall, Call
from eth_typing import HexAddress
from multicall import Multicall
from safe_eth.eth.constants import NULL_ADDRESS
from web3 import Web3

from eth_defi.lagoon.vault import LagoonVault
from eth_defi.provider.broken_provider import get_almost_latest_block_number
from eth_defi.token import TokenDetails
from eth_defi.trace import assert_transaction_success_with_explanation
from eth_defi.uniswap_v2.constants import UNISWAP_V2_DEPLOYMENTS
from eth_defi.uniswap_v2.deployment import fetch_deployment, UniswapV2Deployment
from eth_defi.vault.base import TradingUniverse
Expand Down Expand Up @@ -198,17 +200,23 @@ def test_lagoon_diagnose_routes(
assert routes.loc["DINO -> USDC"]["Value"] == "-"


def test_lagoon_post_valuation_commitee(
def test_lagoon_post_valuation(
web3: Web3,
lagoon_vault: LagoonVault,
base_usdc: TokenDetails,
base_weth: TokenDetails,
base_dino: TokenDetails,
uniswap_v2: UniswapV2Deployment,
topped_up_valuation_manager: HexAddress,
):
"""Update vault NAV."""

vault = lagoon_vault
valuation_manager = topped_up_valuation_manager

# Check value before update
nav = vault.fetch_nav()
assert nav == pytest.approx(Decimal(0.1))

universe = TradingUniverse(
spot_token_addresses={
Expand All @@ -234,7 +242,14 @@ def test_lagoon_post_valuation_commitee(
portfolio_valuation = nav_calculator.calculate_market_sell_nav(portfolio)

total_value = portfolio_valuation.get_total_equity()
tx_data = vault.post_valuation_commitee(total_value)
bound_func = vault.post_new_valuation(total_value)

# Unlocked by anvil
tx_hash = bound_func.transact({"from": valuation_manager})
assert_transaction_success_with_explanation(web3, tx_hash)

# Check value after update
nav = vault.fetch_nav()
assert nav == pytest.approx(Decimal(0.1))


0 comments on commit 7fc01c2

Please sign in to comment.