Maker and taker bonds OK

This commit is contained in:
Reckless_Satoshi
2022-01-06 13:36:22 -08:00
parent 34e05465c2
commit c0d6236dbb
3 changed files with 53 additions and 28 deletions

View File

@ -11,6 +11,9 @@ BOND_SIZE = float(config('BOND_SIZE'))
MARKET_PRICE_API = config('MARKET_PRICE_API') MARKET_PRICE_API = config('MARKET_PRICE_API')
ESCROW_USERNAME = config('ESCROW_USERNAME') 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_MAKER_BOND_INVOICE = int(config('EXP_MAKER_BOND_INVOICE'))
EXP_TAKER_BOND_INVOICE = int(config('EXP_TAKER_BOND_INVOICE')) EXP_TAKER_BOND_INVOICE = int(config('EXP_TAKER_BOND_INVOICE'))
EXP_TRADE_ESCR_INVOICE = int(config('EXP_TRADE_ESCR_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 False, {'Bad Request':'You are already taker of an order'}
return True, None 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): def take(order, user):
order.taker = user order.taker = user
order.status = Order.Status.TAK 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} return True, {'invoice':order.maker_bond.invoice,'bond_satoshis':order.maker_bond.num_satoshis}
order.satoshis_now = cls.satoshis_now(order) 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!' description = f'RoboSats - Maker bond for order ID {order.id}. These sats will return to you if you do not cheat!'
# Gen HODL Invoice # Gen HODL Invoice
@ -160,16 +171,16 @@ class Logics():
def gen_takerbuyer_hodl_invoice(cls, order, user): def gen_takerbuyer_hodl_invoice(cls, order, user):
# Do not gen and cancel if a taker invoice is there and older than 2 minutes # 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: 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) 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!' description = f'RoboSats - Taker bond for order ID {order.id}. These sats will return to you if you do not cheat!'
# Gen HODL Invoice # Gen HODL Invoice
@ -188,4 +199,4 @@ class Logics():
expires_at = expires_at) expires_at = expires_at)
order.save() order.save()
return True, {'invoice':invoice,'bond_satoshis':bond_satoshis} return True, {'invoice':invoice,'bond_satoshis': bond_satoshis}

View File

@ -1,5 +1,5 @@
from rest_framework import serializers from rest_framework import serializers
from .models import Order, LNPayment from .models import Order
class ListOrderSerializer(serializers.ModelSerializer): class ListOrderSerializer(serializers.ModelSerializer):
class Meta: class Meta:
@ -13,5 +13,5 @@ class MakeOrderSerializer(serializers.ModelSerializer):
class UpdateOrderSerializer(serializers.Serializer): class UpdateOrderSerializer(serializers.Serializer):
invoice = serializers.CharField(max_length=300, allow_null=True, allow_blank=True, default=None) 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) rating = serializers.ChoiceField(choices=('1','2','3','4','5'), allow_null=True, allow_blank=True, default=None)

View File

@ -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.generics import CreateAPIView, ListAPIView
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework import viewsets
from rest_framework.response import Response from rest_framework.response import Response
from django.contrib.auth import authenticate, login, logout 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 .serializers import ListOrderSerializer, MakeOrderSerializer, UpdateOrderSerializer
from .models import Order from .models import Order
from .logics import EXP_MAKER_BOND_INVOICE, Logics from .logics import Logics
from .nick_generator.nick_generator import NickGenerator from .nick_generator.nick_generator import NickGenerator
from robohash import Robohash from robohash import Robohash
@ -20,7 +19,6 @@ import hashlib
from pathlib import Path from pathlib import Path
from datetime import timedelta from datetime import timedelta
from django.utils import timezone from django.utils import timezone
from decouple import config from decouple import config
EXP_MAKER_BOND_INVOICE = int(config('EXP_MAKER_BOND_INVOICE')) EXP_MAKER_BOND_INVOICE = int(config('EXP_MAKER_BOND_INVOICE'))
@ -47,7 +45,7 @@ class OrderMakerView(CreateAPIView):
is_explicit = serializer.data.get('is_explicit') is_explicit = serializer.data.get('is_explicit')
valid, context = Logics.validate_already_maker_or_taker(request.user) 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 # Creates a new order
order = Order( order = Order(
@ -58,10 +56,14 @@ class OrderMakerView(CreateAPIView):
premium=premium, premium=premium,
satoshis=satoshis, satoshis=satoshis,
is_explicit=is_explicit, 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) maker=request.user)
order.last_satoshis = order.t0_satoshis = Logics.satoshis_now(order) # TODO move to Order class method when new instance is created! # TODO move to Order class method when new instance is created!
order.last_satoshis = order.t0_satoshis = Logics.satoshis_now(order)
valid, context = Logics.validate_order_size(order)
if not valid: return Response(context, status.HTTP_400_BAD_REQUEST)
order.save() order.save()
return Response(ListOrderSerializer(order).data, status=status.HTTP_201_CREATED) 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. # 4) If status is 'waiting for maker bond', reply with a MAKER HODL invoice.
if order.status == Order.Status.WFB and data['is_maker']: if order.status == Order.Status.WFB and data['is_maker']:
valid, context = Logics.gen_maker_hodl_invoice(order, request.user) 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. # 5) If status is 'Taken' 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']: elif order.status == Order.Status.TAK and data['is_taker'] and data['is_buyer']:
valid, context = Logics.gen_takerbuyer_hodl_invoice(order, request.user) 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. # 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']: elif order.status == Order.Status.PUB and data['is_taker'] and data['is_seller']:
valid, context = Logics.gen_seller_hodl_invoice(order, request.user) 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. # 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']: 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) 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(data, status.HTTP_200_OK)
return Response({'Order Not Found':'Invalid Order Id'},status=status.HTTP_404_NOT_FOUND) return Response({'Order Not Found':'Invalid Order Id'}, status.HTTP_404_NOT_FOUND)
def take_update_confirm_dispute_cancel(self, request, format=None): def take_update_confirm_dispute_cancel(self, request, format=None):
order_id = request.GET.get(self.lookup_url_kwarg) order_id = request.GET.get(self.lookup_url_kwarg)
@ -140,7 +154,7 @@ class OrderView(viewsets.ViewSet):
order = Order.objects.get(id=order_id) 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') action = serializer.data.get('action')
invoice = serializer.data.get('invoice') invoice = serializer.data.get('invoice')
rating = serializer.data.get('rating') rating = serializer.data.get('rating')
@ -232,7 +246,7 @@ class UserView(APIView):
with open(image_path, "wb") as f: with open(image_path, "wb") as f:
rh.img.save(f, format="png") 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: if len(User.objects.filter(username=nickname)) == 0:
User.objects.create_user(username=nickname, password=token, is_staff=False) User.objects.create_user(username=nickname, password=token, is_staff=False)
user = authenticate(request, username=nickname, password=token) user = authenticate(request, username=nickname, password=token)