feat: add rub quote
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
from decimal import Decimal, ROUND_UP
|
||||
from decimal import Decimal, ROUND_DOWN, ROUND_UP
|
||||
from src.application.contracts import ICache, ILogger
|
||||
from src.application.domain.exceptions import OrderTotalOutOfRangeException, ServiceUnavailableException
|
||||
|
||||
|
||||
_MIN_USDT_AMOUNT: Decimal = Decimal('1')
|
||||
|
||||
_MAX_TOTAL_RUB: Decimal = Decimal('600000')
|
||||
|
||||
_FEE_TIERS: tuple[tuple[Decimal, Decimal, Decimal, bool, bool], ...] = (
|
||||
(Decimal('0.08'), Decimal('0'), Decimal('30000'), True, True),
|
||||
(Decimal('0.06'), Decimal('30000'), Decimal('100000'), False, True),
|
||||
@@ -51,12 +53,7 @@ class PaymentQuoteService:
|
||||
self._logger = logger
|
||||
|
||||
|
||||
async def get_quote(self, usdt_amount: Decimal) -> PaymentQuote:
|
||||
if usdt_amount < _MIN_USDT_AMOUNT:
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total is below minimum allowed amount',
|
||||
)
|
||||
|
||||
async def _load_prices(self) -> tuple[Decimal, Decimal]:
|
||||
rate_raw = await self._remote_cache.hget('tradex:rub:rate', 'value')
|
||||
gas_raw = await self._remote_cache.hget('gwei:eth:last', 'normal_rub')
|
||||
self._logger.info(f'Actual market values: rate={rate_raw}, gas={gas_raw}')
|
||||
@@ -71,8 +68,16 @@ class PaymentQuoteService:
|
||||
|
||||
gas_fee = Decimal(gas_raw).quantize(Decimal('0.00'), rounding=ROUND_UP)
|
||||
usdt_exchange_rate = Decimal(rate_raw).quantize(Decimal('0.00'), rounding=ROUND_UP)
|
||||
base_rub = usdt_amount * usdt_exchange_rate
|
||||
return usdt_exchange_rate, gas_fee
|
||||
|
||||
|
||||
def _compose_quote(
|
||||
self,
|
||||
usdt_amount: Decimal,
|
||||
usdt_exchange_rate: Decimal,
|
||||
gas_fee: Decimal,
|
||||
) -> PaymentQuote | None:
|
||||
base_rub = usdt_amount * usdt_exchange_rate
|
||||
chosen_rate: Decimal | None = None
|
||||
service_fee: Decimal | None = None
|
||||
total_price: Decimal | None = None
|
||||
@@ -87,9 +92,7 @@ class PaymentQuoteService:
|
||||
break
|
||||
|
||||
if chosen_rate is None or service_fee is None or total_price is None:
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total exceeds maximum allowed amount',
|
||||
)
|
||||
return None
|
||||
|
||||
return PaymentQuote(
|
||||
usdt_amount=usdt_amount,
|
||||
@@ -100,3 +103,86 @@ class PaymentQuoteService:
|
||||
service_fee_rate=chosen_rate,
|
||||
created_at=datetime.now(timezone.utc),
|
||||
)
|
||||
|
||||
|
||||
async def get_quote(self, usdt_amount: Decimal) -> PaymentQuote:
|
||||
if usdt_amount < _MIN_USDT_AMOUNT:
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total is below minimum allowed amount',
|
||||
)
|
||||
|
||||
usdt_exchange_rate, gas_fee = await self._load_prices()
|
||||
quote = self._compose_quote(usdt_amount, usdt_exchange_rate, gas_fee)
|
||||
if quote is None:
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total exceeds maximum allowed amount',
|
||||
)
|
||||
return quote
|
||||
|
||||
|
||||
async def get_quote_from_total_rub(self, total_rub: Decimal) -> PaymentQuote:
|
||||
if total_rub <= Decimal('0'):
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total is below minimum allowed amount',
|
||||
)
|
||||
|
||||
if total_rub > _MAX_TOTAL_RUB:
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total exceeds maximum allowed amount',
|
||||
)
|
||||
|
||||
usdt_exchange_rate, gas_fee = await self._load_prices()
|
||||
|
||||
if total_rub <= gas_fee:
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total is below minimum allowed amount',
|
||||
)
|
||||
|
||||
min_quote = self._compose_quote(_MIN_USDT_AMOUNT, usdt_exchange_rate, gas_fee)
|
||||
if min_quote is None or min_quote.total_price > total_rub:
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total is below minimum allowed amount',
|
||||
)
|
||||
|
||||
u_budget = ((total_rub - gas_fee) / usdt_exchange_rate).quantize(Decimal('0.01'), rounding=ROUND_DOWN)
|
||||
u_cap = ((_MAX_TOTAL_RUB - gas_fee) / (usdt_exchange_rate * Decimal('1.04'))).quantize(
|
||||
Decimal('0.01'),
|
||||
rounding=ROUND_DOWN,
|
||||
)
|
||||
u_upper = min(u_budget, u_cap)
|
||||
n_hi = int((u_upper * 100).to_integral_value(rounding=ROUND_DOWN))
|
||||
if n_hi < 100:
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total is below minimum allowed amount',
|
||||
)
|
||||
|
||||
n_lo = 100
|
||||
best_cent: int | None = None
|
||||
while n_lo <= n_hi:
|
||||
mid = (n_lo + n_hi) // 2
|
||||
u = Decimal(mid) / Decimal(100)
|
||||
quote = self._compose_quote(u, usdt_exchange_rate, gas_fee)
|
||||
if quote is None:
|
||||
n_hi = mid - 1
|
||||
continue
|
||||
if quote.total_price <= total_rub:
|
||||
best_cent = mid
|
||||
n_lo = mid + 1
|
||||
else:
|
||||
n_hi = mid - 1
|
||||
|
||||
if best_cent is None:
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total is below minimum allowed amount',
|
||||
)
|
||||
|
||||
final = self._compose_quote(
|
||||
Decimal(best_cent) / Decimal(100),
|
||||
usdt_exchange_rate,
|
||||
gas_fee,
|
||||
)
|
||||
if final is None:
|
||||
raise OrderTotalOutOfRangeException(
|
||||
message='Order total exceeds maximum allowed amount',
|
||||
)
|
||||
return final
|
||||
|
||||
Reference in New Issue
Block a user