Skip to content

Commit

Permalink
First test runs
Browse files Browse the repository at this point in the history
  • Loading branch information
miohtama committed Nov 28, 2024
1 parent a25ad93 commit d779810
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 86 deletions.
98 changes: 16 additions & 82 deletions eth_defi/lagoon/vault.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
from functools import cached_property

from eth_typing import HexAddress, BlockIdentifier
from web3 import Web3

from eth_defi.vault.base import VaultBase, VaultSpec, VaultInfo
from eth_defi.balances import fetch_erc20_balances_fallback
from eth_defi.vault.base import VaultBase, VaultSpec, VaultInfo, TradingUniverse, VaultPortfolio


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

#: Address of the Safe multisig the vault is build around
safe_address: HexAddress


class LagoonVault(VaultBase):
Expand All @@ -23,6 +29,7 @@ def __init__(
assert isinstance(web3, Web3)
assert isinstance(spec, VaultSpec)
self.web3 = web3
self.spec = spec

def has_block_range_event_support(self):
return True
Expand All @@ -32,27 +39,22 @@ def get_flow_manager(self):

def fetch_info(self) -> LagoonVaultInfo:
"""Read vault parameters from the chain."""



raise NotImplementedError("Velvet does not support fetching info yet")
return {
"safe_address": self.spec.vault_address,
}

@cached_property
def info(self) -> VelvetVaultInfo:
def info(self) -> LagoonVaultInfo:
return self.fetch_info()

@property
def vault_address(self) -> HexAddress:
return self.info["vaultAddress"]
def safe_address(self) -> HexAddress:
return self.info["safe_address"]

@property
def owner_address(self) -> HexAddress:
return self.info["owner"]

@property
def portfolio_address(self) -> HexAddress:
return self.info["portfolio"]

@property
def name(self) -> str:
return self.info["name"]
Expand All @@ -73,7 +75,7 @@ def fetch_portfolio(

vault_address = self.info["vaultAddress"]

erc20_balances = fetch_erc20_balances_multicall(
erc20_balances = fetch_erc20_balances_fallback(
self.web3,
vault_address,
universe.spot_token_addresses,
Expand All @@ -83,71 +85,3 @@ def fetch_portfolio(
return VaultPortfolio(
spot_erc20=erc20_balances,
)

def prepare_swap_with_enso(
self,
token_in: HexAddress | str,
token_out: HexAddress | str,
swap_amount: int,
slippage: float,
remaining_tokens: set,
swap_all=False,
from_: HexAddress | str | None = None,
) -> dict:
"""Prepare a swap transaction using Enso intent engine and Vevlet API.
:param from_:
Fill int the from field for the tx data.
Used with Anvil and unlocked accounts.
"""

if swap_all:
remaining_tokens.remove(token_in)

tx_data = swap_with_velvet_and_enso(
rebalance_address=self.info["rebalancing"],
owner_address=self.owner_address,
token_in=token_in,
token_out=token_out,
swap_amount=swap_amount,
slippage=slippage,
remaining_tokens=remaining_tokens,
chain_id=self.web3.eth.chain_id,
)

if from_:
tx_data["from"] = Web3.to_checksum_address(from_)

return tx_data

def prepare_deposit_with_enso(
self,
from_: HexAddress | str,
deposit_token_address: HexAddress | str,
amount: int,
):
"""Prepare a deposit transaction with Enso intents.
- Velvet trades any incoming assets and distributes them on open positions
"""
tx_data = deposit_to_velvet(
portfolio=self.portfolio_address,
from_address=from_,
deposit_token_address=deposit_token_address,
amount=amount,
chain_id=self.web3.eth.chain_id,
)
return tx_data

def _make_api_request(
self,
endpoint: str,
params: dict | None = None,
) -> dict:
url = f"{self.api_url}/{endpoint}"
resp = self.session.get(url)
resp.raise_for_status()
data = resp.json()
return data

108 changes: 108 additions & 0 deletions tests/lagoon/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""Base mainnet fork based tests for Lagoon"""
import os

import pytest
from eth_typing import HexAddress
from web3 import Web3

from eth_defi.hotwallet import HotWallet
from eth_defi.lagoon.vault import LagoonVault
from eth_defi.provider.anvil import AnvilLaunch, fork_network_anvil
from eth_defi.provider.multi_provider import create_multi_provider_web3
from eth_defi.token import TokenDetails, fetch_erc20_details
from eth_defi.trace import assert_transaction_success_with_explanation
from eth_defi.vault.base import VaultSpec

JSON_RPC_BASE = os.environ.get("JSON_RPC_BASE")

CI = os.environ.get("CI", None) is not None

pytestmark = pytest.mark.skipif(not JSON_RPC_BASE, reason="No JSON_RPC_BASE environment variable")


@pytest.fixture()
def vault_owner() -> HexAddress:
# Vaut owner
return "0x0c9db006f1c7bfaa0716d70f012ec470587a8d4f"


@pytest.fixture()
def usdc_holder() -> HexAddress:
# https://basescan.org/token/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913#balances
return "0x3304E22DDaa22bCdC5fCa2269b418046aE7b566A"



@pytest.fixture()
def anvil_base_fork(request, vault_owner, usdc_holder, deposit_user) -> AnvilLaunch:
"""Create a testable fork of live BNB chain.
:return: JSON-RPC URL for Web3
"""
launch = fork_network_anvil(
JSON_RPC_BASE,
unlocked_addresses=[vault_owner, usdc_holder, deposit_user],
)
try:
yield launch
finally:
# Wind down Anvil process after the test is complete
launch.close()


@pytest.fixture()
def web3(anvil_base_fork) -> Web3:
web3 = create_multi_provider_web3(anvil_base_fork.json_rpc_url)
assert web3.eth.chain_id == 8453
return web3


@pytest.fixture()
def usdc(web3) -> TokenDetails:
return fetch_erc20_details(
web3,
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
)


@pytest.fixture()
def hot_wallet_user(web3, usdc, usdc_holder) -> HotWallet:
"""A test account with USDC balance."""

hw = HotWallet.create_for_testing(
web3,
test_account_n=1,
eth_amount=10
)
hw.sync_nonce(web3)

# give hot wallet some native token
web3.eth.send_transaction(
{
"from": web3.eth.accounts[9],
"to": hw.address,
"value": 1 * 10**18,
}
)

# Top up with 999 USDC
tx_hash = usdc.contract.functions.transfer(hw.address, 999 * 10**6).transact({"from": usdc_holder, "gas": 100_000})
assert_transaction_success_with_explanation(web3, tx_hash)
return hw


@pytest.fixture()
def base_test_vault_spec() -> VaultSpec:
"""Vault https://dapp.velvet.capital/ManagerVaultDetails/0x205e80371f6d1b33dff7603ca8d3e92bebd7dc25"""
return VaultSpec(1, "0x205e80371f6d1b33dff7603ca8d3e92bebd7dc25")


@pytest.fixture()
def lagoon_vault(web3, base_test_vault_spec: VaultSpec) -> LagoonVault:
return LagoonVault(web3, base_test_vault_spec)


@pytest.fixture()
def deposit_user() -> HexAddress:
"""A user that has preapproved 5 USDC deposit for the vault above, no approve(0 needed."""
return "0x7612A94AafF7a552C373e3124654C1539a4486A8"
8 changes: 8 additions & 0 deletions tests/lagoon/test_lagoon_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from eth_defi.lagoon.vault import LagoonVault


def test_lagoon_info(lagoon_vault: LagoonVault):
vault = lagoon_vault
info = vault.fetch_info()
assert info["safe_address"] == "0x205e80371f6d1b33dff7603ca8d3e92bebd7dc25"

4 changes: 0 additions & 4 deletions tests/velvet/test_velvet_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import os
from decimal import Decimal

import flaky
import pytest
from eth_typing import HexAddress
from web3 import Web3
Expand All @@ -21,9 +20,6 @@
from eth_defi.provider.multi_provider import create_multi_provider_web3
from eth_defi.token import TokenDetails, fetch_erc20_details
from eth_defi.trace import assert_transaction_success_with_explanation
from eth_defi.uniswap_v3.constants import UNISWAP_V3_DEPLOYMENTS
from eth_defi.uniswap_v3.deployment import UniswapV3Deployment, fetch_deployment
from eth_defi.uniswap_v3.price import estimate_buy_received_amount
from eth_defi.vault.base import VaultSpec, TradingUniverse
from eth_defi.velvet import VelvetVault

Expand Down

0 comments on commit d779810

Please sign in to comment.