feat: add withdraw

This commit is contained in:
2026-06-21 14:16:20 +03:00
parent 8f60637e8d
commit 56841b83ce
32 changed files with 1291 additions and 44 deletions

View File

@@ -0,0 +1,64 @@
from __future__ import annotations
from dataclasses import dataclass
from decimal import Decimal,ROUND_DOWN,ROUND_UP
from src.application.contracts import ICache,ILogger
from src.application.domain.exceptions import OrderTotalOutOfRangeException,ServiceUnavailableException
MIN_SBP_WITHDRAWAL_USDT: Decimal=Decimal('100')
MAX_SBP_WITHDRAWAL_USDT: Decimal=Decimal('5000')
_FEE_TIERS: tuple[tuple[Decimal,Decimal,Decimal],...]=(
(Decimal('100'),Decimal('2000'),Decimal('0.08')),
(Decimal('2000.01'),Decimal('4000'),Decimal('0.06')),
(Decimal('4000.01'),Decimal('5000'),Decimal('0.04')),
)
@dataclass(slots=True)
class SbpWithdrawalQuote:
usdt_amount: Decimal
rub_amount: Decimal
usdt_exchange_rate: Decimal
service_fee_rate: Decimal
service_fee_usdt: Decimal
class SbpWithdrawalQuoteService:
def __init__(self,*,remote_cache: ICache,logger: ILogger):
self._remote_cache=remote_cache
self._logger=logger
async def _load_rate(self) -> Decimal:
rate_raw=await self._remote_cache.hget('tradex:rub:rate','value')
self._logger.info(f'Actual market value for SBP withdrawal: rate={rate_raw}')
if rate_raw is None:
raise ServiceUnavailableException(message='Exchange rate unavailable')
return Decimal(rate_raw).quantize(Decimal('0.00'),rounding=ROUND_UP)
@staticmethod
def _fee_rate(usdt_amount: Decimal) -> Decimal:
for lo,hi,rate in _FEE_TIERS:
if lo<=usdt_amount<=hi:
return rate
raise OrderTotalOutOfRangeException(message='Withdrawal amount is out of range')
async def get_quote(self,usdt_amount: Decimal) -> SbpWithdrawalQuote:
if usdt_amount<MIN_SBP_WITHDRAWAL_USDT:
raise OrderTotalOutOfRangeException(message='Withdrawal amount is below minimum allowed amount')
if usdt_amount>MAX_SBP_WITHDRAWAL_USDT:
raise OrderTotalOutOfRangeException(message='Withdrawal amount exceeds maximum allowed amount')
rate=await self._load_rate()
fee_rate=self._fee_rate(usdt_amount)
service_fee=(usdt_amount*fee_rate).quantize(Decimal('0.01'))
payout_usdt=(usdt_amount-service_fee).quantize(Decimal('0.01'))
rub_amount=(payout_usdt*rate).quantize(Decimal('1'),rounding=ROUND_DOWN)
return SbpWithdrawalQuote(
usdt_amount=usdt_amount,
rub_amount=rub_amount,
usdt_exchange_rate=rate,
service_fee_rate=fee_rate,
service_fee_usdt=service_fee,
)