Skip to content

Commit

Permalink
rework getting dashboard data
Browse files Browse the repository at this point in the history
  • Loading branch information
djeck1432 committed Dec 2, 2024
1 parent 7afb090 commit 677780a
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 210 deletions.
466 changes: 374 additions & 92 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ psycopg2 = "^2.9.10"
faker = "^30.8.1"
notebook = "^7.2.2"
sentry-sdk = {extras = ["fastapi"], version = "^2.18.0"}
pragma-sdk = "^2.4.6"

[tool.poetry.group.dev.dependencies]
black = "24.8.0"
Expand Down
7 changes: 4 additions & 3 deletions web_app/api/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from fastapi import APIRouter
from web_app.api.serializers.dashboard import DashboardResponse
from web_app.contract_tools.mixins.dashboard import DashboardMixin
from web_app.contract_tools.mixins import DashboardMixin, HealthRatioMixin
from web_app.db.crud import PositionDBConnector

router = APIRouter()
Expand Down Expand Up @@ -40,13 +40,14 @@ async def get_dashboard(wallet_id: str) -> DashboardResponse:
)
opened_positions = position_db_connector.get_positions_by_wallet_id(wallet_id)

# At the moment, we only support one position per wallet
first_opened_position = (
opened_positions[0]
if opened_positions
else collections.defaultdict(lambda: None)
)
# Fetch zkLend position for the wallet ID
zklend_position = await DashboardMixin.get_zklend_position(contract_address)
health_ratio = await HealthRatioMixin.get_health_ratio(contract_address, first_opened_position.token_symbol)

# Fetch balances (assuming you have a method for this)
wallet_balances = await DashboardMixin.get_wallet_balances(wallet_id)
Expand All @@ -57,9 +58,9 @@ async def get_dashboard(wallet_id: str) -> DashboardResponse:
)
return DashboardResponse(
balances=wallet_balances,
health_ratio=health_ratio,
multipliers={"ETH": first_opened_position["multiplier"]},
start_dates={"ETH": first_opened_position["created_at"]},
zklend_position=zklend_position,
current_sum=current_sum,
start_sum=start_sum,
)
105 changes: 5 additions & 100 deletions web_app/api/serializers/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,108 +4,18 @@

from datetime import datetime
from decimal import Decimal
from typing import Dict, List, Optional, Any
from typing import Dict, Any

from pydantic import BaseModel, Field, RootModel, field_validator
from web_app.contract_tools.constants import TokenParams


class Data(BaseModel):
"""
Data class for position details.
"""

collateral: bool
debt: bool


class TotalBalances(RootModel):
"""
TotalBalances class for total balances.
"""

# Since the keys are dynamic (addresses), we use a generic Dict
root: Dict[str, str]


class Position(BaseModel):
"""
Position class for position details.
"""

data: Data
token_address: Optional[str] = Field(None, alias="tokenAddress")
total_balances: TotalBalances = Field(alias="totalBalances")

@field_validator("total_balances", mode="before")
def convert_total_balances(cls, balances):
"""
Convert total_balances to their decimal values based on token decimals.
"""
converted_balances = {}
for token_address, balance in balances.items():
try:
# Fetch the token decimals from TokenParams
decimals = TokenParams.get_token_decimals(token_address)
# Convert the balance using the decimals
converted_balances[token_address] = str(
Decimal(balance) / Decimal(10**decimals)
)
except ValueError as e:
raise ValueError(f"Error in balance conversion: {str(e)}")
return converted_balances

class Config:
"""
Configuration for the Position class.
"""

populate_by_name = True


class Product(BaseModel):
"""
Product class for product details.
"""

name: str
health_ratio: str
positions: List[Position]


class ZkLendPositionResponse(BaseModel):
"""
ZkLendPositionResponse class for ZkLend position details.
"""

products: List[Product] = Field(default_factory=list)

@field_validator("products", mode="before")
def convert_products(cls, products):
"""
Convert products to their respective models.
"""
converted_products = []
for product in products:
groups = product.pop("groups", None)
product["health_ratio"] = groups.get("1", {}).get("healthRatio")
converted_products.append(Product(**product))
# For debugging purposes # noqa: F841 (unused variable)
return converted_products

class Config:
"""
Configuration for the ZkLendPositionResponse class.
"""

populate_by_name = True
from pydantic import BaseModel, Field


class DashboardResponse(BaseModel):
"""
DashboardResponse class for dashboard details.
"""

health_ratio: str = Field(
..., example="0.5", description="The health ratio of the user."
)
balances: Dict[str, Any] = Field(
...,
example={"ETH": 5.0, "USDC": 1000.0},
Expand All @@ -119,11 +29,6 @@ class DashboardResponse(BaseModel):
example={"ETH": "2024-01-01T00:00:00"},
description="The start date for each position.",
)
zklend_position: ZkLendPositionResponse = Field(
...,
example={"ETH": {"borrowed": 5000, "collateral": 10}},
description="Details of the ZkLend position for each asset.",
)
current_sum: Decimal = Field(
...,
example=5000.0,
Expand Down
4 changes: 4 additions & 0 deletions web_app/contract_tools/mixins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .dashboard import DashboardMixin
from .health_ratio import HealthRatioMixin
from .deposit import DepositMixin
from .alert import AlertMixin
19 changes: 4 additions & 15 deletions web_app/contract_tools/mixins/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from web_app.contract_tools.blockchain_call import StarknetClient
from web_app.contract_tools.constants import TokenParams
from web_app.contract_tools.api_request import APIRequest
from web_app.api.serializers.dashboard import ZkLendPositionResponse
from web_app.api.serializers.dashboard import DashboardResponse

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -85,25 +85,14 @@ async def get_wallet_balances(cls, holder_address: str) -> Dict[str, str]:
return wallet_balances

@classmethod
async def get_zklend_position(cls, contract_address: str) -> ZkLendPositionResponse:
async def get_zklend_position(cls, contract_address: str, position: "Position") -> DashboardResponse:
"""
Get the zkLend position for the given wallet ID.
:param contract_address: contract address
:param position: Position db model
:return: zkLend position validated by Pydantic models
"""
modified_contract_address = contract_address[:2] + "0" + contract_address[2:]
response = await APIRequest(base_url=ARGENT_X_POSITION_URL).fetch(
f"decomposition/{modified_contract_address}", params={"chain": "starknet"}
)
if not response:
return ZkLendPositionResponse(products=[])

# Validate the response using Pydantic models
dapps = response.get("dapps", [])
products = cls._get_products(dapps)
zk_lend_position_response = ZkLendPositionResponse(products=products)

return zk_lend_position_response
pass

@classmethod
def _get_products(cls, dapps: list) -> list[dict]:
Expand Down

0 comments on commit 677780a

Please sign in to comment.