feat: add wallets balance

This commit is contained in:
2026-06-10 18:21:23 +03:00
parent 6c3628edfa
commit 15c06fa9cc
11 changed files with 622 additions and 0 deletions

View File

@@ -5,11 +5,13 @@ from src.application.commands import (
GetOrganizationCommand,
GetOrganizationMnemonicCommand,
GetOrganizationSecretKeysCommand,
GetOrganizationWalletBalancesCommand,
GetPurchaseRequestCommand,
ListOrganizationWalletsCommand,
ListPurchaseRequestsCommand,
)
from src.application.contracts import ILogger
from src.infrastructure.wallet_balances import WalletBalanceService
from src.presentation.dependencies.logger import get_logger
from src.presentation.dependencies.unit_of_work import get_unit_of_work
@@ -49,6 +51,17 @@ def get_list_organization_wallets_command(
return ListOrganizationWalletsCommand(unit_of_work=unit_of_work, logger=logger)
def get_get_organization_wallet_balances_command(
logger: ILogger = Depends(get_logger),
unit_of_work: IUnitOfWork = Depends(get_unit_of_work),
) -> GetOrganizationWalletBalancesCommand:
return GetOrganizationWalletBalancesCommand(
unit_of_work=unit_of_work,
balance_service=WalletBalanceService(),
logger=logger,
)
def get_get_organization_mnemonic_command(
logger: ILogger = Depends(get_logger),
unit_of_work: IUnitOfWork = Depends(get_unit_of_work),

View File

@@ -4,6 +4,7 @@ from src.application.commands import (
GetOrganizationCommand,
GetOrganizationMnemonicCommand,
GetOrganizationSecretKeysCommand,
GetOrganizationWalletBalancesCommand,
ListOrganizationWalletsCommand,
)
from src.application.domain.dto import AuthContext
@@ -12,18 +13,21 @@ from src.presentation.dependencies.commands import (
get_get_organization_command,
get_get_organization_mnemonic_command,
get_get_organization_secret_keys_command,
get_get_organization_wallet_balances_command,
get_list_organization_wallets_command,
)
from src.presentation.schemas.organization import (
MnemonicResponse,
OrganizationResponse,
SecretKeyResponse,
WalletBalancesResponse,
WalletResponse,
)
from src.presentation.serializers.organization import (
mnemonic_to_response,
organization_to_response,
secret_key_to_response,
wallet_balances_to_response,
wallet_to_response,
)
@@ -48,6 +52,15 @@ async def list_organization_wallets(
return [wallet_to_response(w) for w in wallets]
@organizations_router.get('/wallets/balances', response_model=WalletBalancesResponse)
async def get_organization_wallet_balances(
auth: AuthContext = Depends(require_access_token),
command: GetOrganizationWalletBalancesCommand = Depends(get_get_organization_wallet_balances_command),
):
balances = await command(auth.user_id)
return wallet_balances_to_response(balances)
@organizations_router.get('/wallets/mnemonic', response_model=MnemonicResponse)
async def get_organization_mnemonic(
auth: AuthContext = Depends(require_access_token),

View File

@@ -35,6 +35,32 @@ class WalletResponse(BaseModel):
created_at: str | None
class FormattedAmountResponse(BaseModel):
raw: str
formatted: str
decimals: int
usd_price: str | None
usd_value: str | None
class WalletBalanceResponse(BaseModel):
id: str
chain: str
address: str
derivation_path: str
native_symbol: str
native: FormattedAmountResponse
tokens: dict[str, FormattedAmountResponse]
total_usd: str | None
error: str | None
class WalletBalancesResponse(BaseModel):
items: list[WalletBalanceResponse]
total_usd: str | None
has_errors: bool
class MnemonicResponse(BaseModel):
mnemonic: str

View File

@@ -3,11 +3,15 @@ from __future__ import annotations
from src.application.domain.entities.legal_entity import LegalEntityEntity
from src.application.domain.entities.organization_wallet import OrganizationWalletEntity
from src.presentation.schemas.organization import (
FormattedAmountResponse,
MnemonicResponse,
OrganizationResponse,
SecretKeyResponse,
WalletBalanceResponse,
WalletBalancesResponse,
WalletResponse,
)
from src.infrastructure.wallet_balances.service import FormattedAmount, WalletBalance
def organization_to_response(entity: LegalEntityEntity) -> OrganizationResponse:
@@ -44,6 +48,40 @@ def wallet_to_response(entity: OrganizationWalletEntity) -> WalletResponse:
)
def formatted_amount_to_response(amount: FormattedAmount) -> FormattedAmountResponse:
return FormattedAmountResponse(
raw=amount.raw,
formatted=amount.formatted,
decimals=amount.decimals,
usd_price=str(amount.usd_price) if amount.usd_price is not None else None,
usd_value=str(amount.usd_value) if amount.usd_value is not None else None,
)
def wallet_balance_to_response(entity: WalletBalance) -> WalletBalanceResponse:
return WalletBalanceResponse(
id=entity.wallet_id,
chain=entity.chain,
address=entity.address,
derivation_path=entity.derivation_path,
native_symbol=entity.native_symbol,
native=formatted_amount_to_response(entity.native),
tokens={k: formatted_amount_to_response(v) for k, v in entity.tokens.items()},
total_usd=str(entity.total_usd) if entity.total_usd is not None else None,
error=entity.error,
)
def wallet_balances_to_response(items: list[WalletBalance]) -> WalletBalancesResponse:
values = [item.total_usd for item in items if item.total_usd is not None]
total = sum(values) if values else None
return WalletBalancesResponse(
items=[wallet_balance_to_response(item) for item in items],
total_usd=str(total) if total is not None else None,
has_errors=any(item.error is not None for item in items),
)
def mnemonic_to_response(mnemonic: str) -> MnemonicResponse:
return MnemonicResponse(mnemonic=mnemonic)