From 76a45bc8d5ac874f85099c52fc470c38faed043b Mon Sep 17 00:00:00 2001 From: Reckless_Satoshi Date: Sat, 1 Jan 2022 14:34:23 -0800 Subject: [PATCH] Create API endpoint for POST make order Added the Order model with a rough approxiation to the fields needed. The field status can be read lineally as the progression trough the app pipeline. The view serves POSTs requests to enter new orders into the db. --- .gitignore | 2 ++ api/models.py | 70 +++++++++++++++++++++++++++++++++++++++++++- api/serializers.py | 12 ++++++++ api/urls.py | 6 ++++ api/views.py | 39 +++++++++++++++++++++++- robosats/settings.py | 2 ++ robosats/urls.py | 5 ++-- 7 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 api/serializers.py create mode 100644 api/urls.py diff --git a/.gitignore b/.gitignore index 96ea0573..21c16b36 100755 --- a/.gitignore +++ b/.gitignore @@ -635,3 +635,5 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd +# Django +*migrations* diff --git a/api/models.py b/api/models.py index 71a83623..60ed9c1a 100644 --- a/api/models.py +++ b/api/models.py @@ -1,3 +1,71 @@ from django.db import models +from django.contrib.auth.models import User +from django.core.validators import MaxValueValidator, MinValueValidator -# Create your models here. +############################# +# TODO +# Load hparams from .env file + +min_satoshis_trade = 10*1000 +max_satoshis_trade = 500*1000 + +class Order(models.Model): + + class Types(models.IntegerChoices): + BUY = 0, 'BUY' + SELL = 1, 'SELL' + + class Currencies(models.IntegerChoices): + USD = 1, 'USD' + EUR = 2, 'EUR' + ETH = 3, 'ETH' + + class Status(models.TextChoices): + WFB = 0, 'Waiting for bond' + PUB = 1, 'Published in order book' + DEL = 2, 'Deleted from order book' + TAK = 3, 'Taken' + UCA = 4, 'Unilaterally cancelled' + RET = 5, 'Returned to order book' # Probably same as 1 in most cases. + WF2 = 6, 'Waiting for trade collateral and buyer invoice' + WTC = 7, 'Waiting only for trade collateral' + WBI = 8, 'Waiting only for buyer invoice' + EXF = 9, 'Exchanging fiat / In chat' + CCA = 10, 'Collaboratively cancelled' + FSE = 11, 'Fiat sent' + FCO = 12, 'Fiat confirmed' + SUC = 13, 'Sucessfully settled' + FAI = 14, 'Failed lightning network routing' + UPI = 15, 'Updated invoice' + DIS = 16, 'In dispute' + MLD = 17, 'Maker lost dispute' + TLD = 18, 'Taker lost dispute' + EXP = 19, 'Expired' + + # order info, id = models.CharField(max_length=64, unique=True, null=False) + status = models.PositiveSmallIntegerField(choices=Status.choices, default=Status.WFB) + created_at = models.DateTimeField(auto_now_add=True) + + # order details + type = models.PositiveSmallIntegerField(choices=Types.choices, null=False) + currency = models.PositiveSmallIntegerField(choices=Currencies.choices, null=False) + amount = models.DecimalField(max_digits=9, decimal_places=4, validators=[MinValueValidator(0.00001)]) + premium = models.DecimalField(max_digits=3, decimal_places=2, default=0, null=True, validators=[MinValueValidator(-100), MaxValueValidator(1000)]) + satoshis = models.PositiveBigIntegerField(null=True, validators=[MinValueValidator(min_satoshis_trade), MaxValueValidator(max_satoshis_trade)]) + + # order participants + maker = models.ForeignKey(User, related_name='maker', on_delete=models.SET_NULL, null=True, default=None) # unique = True, a maker can only make one order + taker = models.ForeignKey(User, related_name='taker', on_delete=models.SET_NULL, null=True, default=None) # unique = True, a taker can only take one order + + # order collateral + has_maker_bond = models.BooleanField(default=False, null=False) + has_taker_bond = models.BooleanField(default=False, null=False) + has_trade_collat = models.BooleanField(default=False, null=False) + + maker_bond_secret = models.CharField(max_length=300, unique=False, null=True, default=None) + taker_bond_secret = models.CharField(max_length=300, unique=False, null=True, default=None) + trade_collat_secret = models.CharField(max_length=300, unique=False, null=True, default=None) + + # buyer payment LN invoice + has_invoice = models.BooleanField(default=False, null=False) # has invoice and is valid + invoice = models.CharField(max_length=300, unique=False, null=True, default=None) diff --git a/api/serializers.py b/api/serializers.py new file mode 100644 index 00000000..9435ecdd --- /dev/null +++ b/api/serializers.py @@ -0,0 +1,12 @@ +from rest_framework import serializers +from .models import Order + +class OrderSerializer(serializers.ModelSerializer): + class Meta: + model = Order + fields = ('id','status','created_at','type','currency','amount','premium','satoshis','maker') + +class MakeOrderSerializer(serializers.ModelSerializer): + class Meta: + model = Order + fields = ('type','currency','amount','premium','satoshis') \ No newline at end of file diff --git a/api/urls.py b/api/urls.py new file mode 100644 index 00000000..2ddd2340 --- /dev/null +++ b/api/urls.py @@ -0,0 +1,6 @@ +from django.urls import path +from .views import MakeOrder + +urlpatterns = [ + path('make/', MakeOrder.as_view()) + ] \ No newline at end of file diff --git a/api/views.py b/api/views.py index 91ea44a2..4f68e242 100644 --- a/api/views.py +++ b/api/views.py @@ -1,3 +1,40 @@ -from django.shortcuts import render +from rest_framework import generics, status +from rest_framework.views import APIView +from rest_framework.response import Response + +from .serializers import OrderSerializer, MakeOrderSerializer +from .models import Order # Create your views here. + +class MakeOrder(APIView): + serializer_class = MakeOrderSerializer + + def post(self,request, format=None): + + serializer = self.serializer_class(data=request.data) + print(serializer) + if serializer.is_valid(): + otype = serializer.data.get('type') + currency = serializer.data.get('currency') + amount = serializer.data.get('amount') + premium = serializer.data.get('premium') + satoshis = serializer.data.get('satoshis') + + ################# + # TODO + # query if the user is already a maker or taker, return error + + # Creates a new order in db + order = Order( + type=otype, + currency=currency, + amount=amount, + premium=premium, + satoshis=satoshis) + order.save() + + if not serializer.is_valid(): + return Response(status=status.HTTP_400_BAD_REQUEST) + + return Response(OrderSerializer(order).data, status=status.HTTP_201_CREATED) \ No newline at end of file diff --git a/robosats/settings.py b/robosats/settings.py index c50c5079..8617533e 100644 --- a/robosats/settings.py +++ b/robosats/settings.py @@ -37,6 +37,8 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', + 'api', ] MIDDLEWARE = [ diff --git a/robosats/urls.py b/robosats/urls.py index 83b304db..dde1abba 100644 --- a/robosats/urls.py +++ b/robosats/urls.py @@ -14,8 +14,9 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path +from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), -] + path('api/', include('api.urls')), + ]