diff --git a/api/logics.py b/api/logics.py index 5fcdfa95..e3ed98c5 100644 --- a/api/logics.py +++ b/api/logics.py @@ -11,6 +11,9 @@ BOND_SIZE = float(config('BOND_SIZE')) MARKET_PRICE_API = config('MARKET_PRICE_API') ESCROW_USERNAME = config('ESCROW_USERNAME') +MIN_TRADE = int(config('MIN_TRADE')) +MAX_TRADE = int(config('MAX_TRADE')) + EXP_MAKER_BOND_INVOICE = int(config('EXP_MAKER_BOND_INVOICE')) EXP_TAKER_BOND_INVOICE = int(config('EXP_TAKER_BOND_INVOICE')) EXP_TRADE_ESCR_INVOICE = int(config('EXP_TRADE_ESCR_INVOICE')) @@ -32,6 +35,14 @@ class Logics(): return False, {'Bad Request':'You are already taker of an order'} return True, None + def validate_order_size(order): + '''Checks if order is withing limits at t0''' + if order.t0_satoshis > MAX_TRADE: + return False, {'Bad_request': 'Your order is too big. It is worth {order.t0_satoshis} now, max is {MAX_TRADE}'} + if order.t0_satoshis < MIN_TRADE: + return False, {'Bad_request': 'Your order is too small. It is worth {order.t0_satoshis} now, min is {MIN_TRADE}'} + return True, None + def take(order, user): order.taker = user order.status = Order.Status.TAK @@ -135,7 +146,7 @@ class Logics(): return True, {'invoice':order.maker_bond.invoice,'bond_satoshis':order.maker_bond.num_satoshis} order.satoshis_now = cls.satoshis_now(order) - bond_satoshis = order.satoshis_now * BOND_SIZE + bond_satoshis = int(order.satoshis_now * BOND_SIZE) description = f'RoboSats - Maker bond for order ID {order.id}. These sats will return to you if you do not cheat!' # Gen HODL Invoice @@ -160,16 +171,16 @@ class Logics(): def gen_takerbuyer_hodl_invoice(cls, order, user): # Do not gen and cancel if a taker invoice is there and older than 2 minutes - if order.taker_bond.created_at < (timezone.now()+timedelta(minutes=EXP_TAKER_BOND_INVOICE)): - cls.cancel_order(order, user, 3) # State 3, cancel order before taker bond - return False, {'Invoice expired':'You did not confirm taking the order in time.'} - - # Return the previous invoice if there was one if order.taker_bond: - return True, {'invoice':order.taker_bond.invoice,'bond_satoshis':order.taker_bond.num_satoshis} + if order.taker_bond.created_at > (timezone.now()+timedelta(minutes=EXP_TAKER_BOND_INVOICE)): + cls.cancel_order(order, user, 3) # State 3, cancel order before taker bond + return False, {'Invoice expired':'You did not confirm taking the order in time.'} + else: + # Return the previous invoice if there was one + return True, {'invoice':order.taker_bond.invoice,'bond_satoshis':order.taker_bond.num_satoshis} order.satoshis_now = cls.satoshis_now(order) - bond_satoshis = order.satoshis_now * BOND_SIZE + bond_satoshis = int(order.satoshis_now * BOND_SIZE) description = f'RoboSats - Taker bond for order ID {order.id}. These sats will return to you if you do not cheat!' # Gen HODL Invoice @@ -188,4 +199,4 @@ class Logics(): expires_at = expires_at) order.save() - return True, {'invoice':invoice,'bond_satoshis':bond_satoshis} \ No newline at end of file + return True, {'invoice':invoice,'bond_satoshis': bond_satoshis} \ No newline at end of file diff --git a/api/serializers.py b/api/serializers.py index d77b1e17..a819db91 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from .models import Order, LNPayment +from .models import Order class ListOrderSerializer(serializers.ModelSerializer): class Meta: @@ -13,5 +13,5 @@ class MakeOrderSerializer(serializers.ModelSerializer): class UpdateOrderSerializer(serializers.Serializer): invoice = serializers.CharField(max_length=300, allow_null=True, allow_blank=True, default=None) - action = serializers.ChoiceField(choices=('take','dispute','cancel','confirm','rate'), allow_null=False) + action = serializers.ChoiceField(choices=('take','update_invoice','dispute','cancel','confirm','rate'), allow_null=False) rating = serializers.ChoiceField(choices=('1','2','3','4','5'), allow_null=True, allow_blank=True, default=None) \ No newline at end of file diff --git a/api/views.py b/api/views.py index 1b8900a1..39ec55d1 100644 --- a/api/views.py +++ b/api/views.py @@ -1,7 +1,6 @@ -from rest_framework import status, serializers +from rest_framework import status, viewsets from rest_framework.generics import CreateAPIView, ListAPIView from rest_framework.views import APIView -from rest_framework import viewsets from rest_framework.response import Response from django.contrib.auth import authenticate, login, logout @@ -9,7 +8,7 @@ from django.contrib.auth.models import User from .serializers import ListOrderSerializer, MakeOrderSerializer, UpdateOrderSerializer from .models import Order -from .logics import EXP_MAKER_BOND_INVOICE, Logics +from .logics import Logics from .nick_generator.nick_generator import NickGenerator from robohash import Robohash @@ -20,7 +19,6 @@ import hashlib from pathlib import Path from datetime import timedelta from django.utils import timezone - from decouple import config EXP_MAKER_BOND_INVOICE = int(config('EXP_MAKER_BOND_INVOICE')) @@ -47,7 +45,7 @@ class OrderMakerView(CreateAPIView): is_explicit = serializer.data.get('is_explicit') valid, context = Logics.validate_already_maker_or_taker(request.user) - if not valid: return Response(context, status=status.HTTP_409_CONFLICT) + if not valid: return Response(context, status.HTTP_409_CONFLICT) # Creates a new order order = Order( @@ -58,10 +56,14 @@ class OrderMakerView(CreateAPIView): premium=premium, satoshis=satoshis, is_explicit=is_explicit, - expires_at=timezone.now()+timedelta(minutes=EXP_MAKER_BOND_INVOICE), + expires_at=timezone.now()+timedelta(minutes=EXP_MAKER_BOND_INVOICE), # TODO Move to class method maker=request.user) + + # TODO move to Order class method when new instance is created! + order.last_satoshis = order.t0_satoshis = Logics.satoshis_now(order) - order.last_satoshis = order.t0_satoshis = Logics.satoshis_now(order) # TODO move to Order class method when new instance is created! + valid, context = Logics.validate_order_size(order) + if not valid: return Response(context, status.HTTP_400_BAD_REQUEST) order.save() return Response(ListOrderSerializer(order).data, status=status.HTTP_201_CREATED) @@ -112,25 +114,37 @@ class OrderView(viewsets.ViewSet): # 4) If status is 'waiting for maker bond', reply with a MAKER HODL invoice. if order.status == Order.Status.WFB and data['is_maker']: valid, context = Logics.gen_maker_hodl_invoice(order, request.user) - data = {**data, **context} if valid else Response(context, status.HTTP_400_BAD_REQUEST) + if valid: + data = {**data, **context} + else: + return Response(context, status.HTTP_400_BAD_REQUEST) - # 5) If status is 'Public' and user is taker/buyer, reply with a TAKER HODL invoice. - elif order.status == Order.Status.PUB and data['is_taker'] and data['is_buyer']: + # 5) If status is 'Taken' and user is taker/buyer, reply with a TAKER HODL invoice. + elif order.status == Order.Status.TAK and data['is_taker'] and data['is_buyer']: valid, context = Logics.gen_takerbuyer_hodl_invoice(order, request.user) - data = {**data, **context} if valid else Response(context, status.HTTP_400_BAD_REQUEST) + if valid: + data = {**data, **context} + else: + return Response(context, status.HTTP_400_BAD_REQUEST) # 6) If status is 'Public' and user is taker/seller, reply with a ESCROW HODL invoice. elif order.status == Order.Status.PUB and data['is_taker'] and data['is_seller']: valid, context = Logics.gen_seller_hodl_invoice(order, request.user) - data = {**data, **context} if valid else Response(context, status.HTTP_400_BAD_REQUEST) + if valid: + data = {**data, **context} + else: + return Response(context, status.HTTP_400_BAD_REQUEST) # 7) If status is 'WF2/WTC' and user is maker/seller, reply with an ESCROW HODL invoice. elif (order.status == Order.Status.WF2 or order.status == Order.Status.WF2) and data['is_maker'] and data['is_seller']: valid, context = Logics.gen_seller_hodl_invoice(order, request.user) - data = {**data, **context} if valid else Response(context, status=status.HTTP_400_BAD_REQUEST) + if valid: + data = {**data, **context} + else: + return Response(context, status.HTTP_400_BAD_REQUEST) - return Response(data, status=status.HTTP_200_OK) - return Response({'Order Not Found':'Invalid Order Id'},status=status.HTTP_404_NOT_FOUND) + return Response(data, status.HTTP_200_OK) + return Response({'Order Not Found':'Invalid Order Id'}, status.HTTP_404_NOT_FOUND) def take_update_confirm_dispute_cancel(self, request, format=None): order_id = request.GET.get(self.lookup_url_kwarg) @@ -140,7 +154,7 @@ class OrderView(viewsets.ViewSet): order = Order.objects.get(id=order_id) - # action is either 1)'take', 2)'confirm', 3)'cancel', 4)'dispute' , 5)'update' (invoice) 6)'rate' (counterparty) + # action is either 1)'take', 2)'confirm', 3)'cancel', 4)'dispute' , 5)'update_invoice' 6)'rate' (counterparty) action = serializer.data.get('action') invoice = serializer.data.get('invoice') rating = serializer.data.get('rating') @@ -232,7 +246,7 @@ class UserView(APIView): with open(image_path, "wb") as f: rh.img.save(f, format="png") - # Create new credentials and logsin if nickname is new + # Create new credentials and log in if nickname is new if len(User.objects.filter(username=nickname)) == 0: User.objects.create_user(username=nickname, password=token, is_staff=False) user = authenticate(request, username=nickname, password=token)