mirror of
https://github.com/RoboSats/robosats.git
synced 2025-07-20 01:33:15 +00:00
Maker and taker bonds OK
This commit is contained in:
@ -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}
|
@ -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)
|
48
api/views.py
48
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.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)
|
||||||
|
Reference in New Issue
Block a user