mirror of
https://github.com/RoboSats/robosats.git
synced 2025-02-22 13:19:02 +00:00
Add more api logics
The workflow is actually more complex than I though. In fact the whole scope of the project greatly surpass my expectation of "weekend project". Want to lay down something functional even if buggy and ugly, I'm a bad coder but this will work out!
This commit is contained in:
parent
6a1a906bea
commit
8a55383761
121
api/logics.py
121
api/logics.py
@ -29,18 +29,18 @@ class Logics():
|
|||||||
'''Checks if the user is already partipant of an order'''
|
'''Checks if the user is already partipant of an order'''
|
||||||
queryset = Order.objects.filter(maker=user)
|
queryset = Order.objects.filter(maker=user)
|
||||||
if queryset.exists():
|
if queryset.exists():
|
||||||
return False, {'Bad Request':'You are already maker of an order'}
|
return False, {'bad_request':'You are already maker of an order'}
|
||||||
queryset = Order.objects.filter(taker=user)
|
queryset = Order.objects.filter(taker=user)
|
||||||
if queryset.exists():
|
if queryset.exists():
|
||||||
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):
|
def validate_order_size(order):
|
||||||
'''Checks if order is withing limits at t0'''
|
'''Checks if order is withing limits at t0'''
|
||||||
if order.t0_satoshis > MAX_TRADE:
|
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}'}
|
return False, {'bad_request': f'Your order is too big. It is worth {order.t0_satoshis} now. But maximum is {MAX_TRADE}'}
|
||||||
if order.t0_satoshis < MIN_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 False, {'bad_request': f'Your order is too small. It is worth {order.t0_satoshis} now. But minimum is {MIN_TRADE}'}
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def take(order, user):
|
def take(order, user):
|
||||||
@ -64,11 +64,12 @@ class Logics():
|
|||||||
satoshis_now = order.satoshis
|
satoshis_now = order.satoshis
|
||||||
else:
|
else:
|
||||||
# TODO Add fallback Public APIs and error handling
|
# TODO Add fallback Public APIs and error handling
|
||||||
|
# Think about polling price data in a different way (e.g. store locally every t seconds)
|
||||||
market_prices = requests.get(MARKET_PRICE_API).json()
|
market_prices = requests.get(MARKET_PRICE_API).json()
|
||||||
exchange_rate = float(market_prices[Order.Currencies(order.currency).label]['last'])
|
exchange_rate = float(market_prices[Order.Currencies(order.currency).label]['last'])
|
||||||
satoshis_now = ((float(order.amount) * 1+float(order.premium)) / exchange_rate) * 100*1000*1000
|
satoshis_now = ((float(order.amount) * 1+float(order.premium)) / exchange_rate) * 100*1000*1000
|
||||||
|
|
||||||
return satoshis_now
|
return int(satoshis_now)
|
||||||
|
|
||||||
def order_expires(order):
|
def order_expires(order):
|
||||||
order.status = Order.Status.EXP
|
order.status = Order.Status.EXP
|
||||||
@ -76,6 +77,16 @@ class Logics():
|
|||||||
order.taker = None
|
order.taker = None
|
||||||
order.save()
|
order.save()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def buyer_invoice_amount(cls, order, user):
|
||||||
|
''' Computes buyer invoice amount. Uses order.last_satoshis,
|
||||||
|
that is the final trade amount set at Taker Bond time'''
|
||||||
|
|
||||||
|
if cls.is_buyer(order, user):
|
||||||
|
invoice_amount = int(order.last_satoshis * (1-FEE)) # Trading FEE is charged here.
|
||||||
|
|
||||||
|
return True, {'invoice_amount': invoice_amount}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_invoice(cls, order, user, invoice):
|
def update_invoice(cls, order, user, invoice):
|
||||||
is_valid_invoice, num_satoshis, description, payment_hash, expires_at = LNNode.validate_ln_invoice(invoice)
|
is_valid_invoice, num_satoshis, description, payment_hash, expires_at = LNNode.validate_ln_invoice(invoice)
|
||||||
@ -117,12 +128,26 @@ class Logics():
|
|||||||
order.save()
|
order.save()
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def rate_counterparty(cls, order, user, rating):
|
||||||
|
# if maker, rates taker
|
||||||
|
if order.maker == user:
|
||||||
|
order.taker.profile.total_ratings = order.taker.profile.total_ratings + 1
|
||||||
|
last_ratings = list(order.taker.profile.last_ratings).append(rating)
|
||||||
|
order.taker.profile.total_ratings = sum(last_ratings) / len(last_ratings)
|
||||||
|
# if taker, rates maker
|
||||||
|
if order.taker == user:
|
||||||
|
order.maker.profile.total_ratings = order.maker.profile.total_ratings + 1
|
||||||
|
last_ratings = list(order.maker.profile.last_ratings).append(rating)
|
||||||
|
order.maker.profile.total_ratings = sum(last_ratings) / len(last_ratings)
|
||||||
|
|
||||||
|
order.save()
|
||||||
|
return True, None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cancel_order(cls, order, user, state):
|
def cancel_order(cls, order, user, state):
|
||||||
|
|
||||||
# 1) When maker cancels before bond
|
# 1) When maker cancels before bond
|
||||||
'''The order never shows up on the book and order
|
'''The order never shows up on the book and order
|
||||||
status becomes "cancelled". That's it.'''
|
status becomes "cancelled". That's it.'''
|
||||||
if order.status == Order.Status.WFB and order.maker == user:
|
if order.status == Order.Status.WFB and order.maker == user:
|
||||||
@ -140,12 +165,12 @@ class Logics():
|
|||||||
''' The order goes back to the book as public.
|
''' The order goes back to the book as public.
|
||||||
LNPayment "order.taker_bond" is deleted() '''
|
LNPayment "order.taker_bond" is deleted() '''
|
||||||
|
|
||||||
# 4) When taker or maker cancel after bond
|
# 4) When taker or maker cancel after bond (before escrow)
|
||||||
'''The order goes into cancelled status if maker cancels.
|
'''The order goes into cancelled status if maker cancels.
|
||||||
The order goes into the public book if taker cancels.
|
The order goes into the public book if taker cancels.
|
||||||
In both cases there is a small fee.'''
|
In both cases there is a small fee.'''
|
||||||
|
|
||||||
# 5) When trade collateral has been posted
|
# 5) When trade collateral has been posted (after escrow)
|
||||||
'''Always goes to cancelled status. Collaboration is needed.
|
'''Always goes to cancelled status. Collaboration is needed.
|
||||||
When a user asks for cancel, 'order.is_pending_cancel' goes True.
|
When a user asks for cancel, 'order.is_pending_cancel' goes True.
|
||||||
When the second user asks for cancel. Order is totally cancelled.
|
When the second user asks for cancel. Order is totally cancelled.
|
||||||
@ -154,22 +179,23 @@ class Logics():
|
|||||||
else:
|
else:
|
||||||
return False, {'bad_request':'You cannot cancel this order'}
|
return False, {'bad_request':'You cannot cancel this order'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def gen_maker_hodl_invoice(cls, order, user):
|
def gen_maker_hodl_invoice(cls, order, user):
|
||||||
|
|
||||||
# Do not gen and delete if order is more than 5 minutes old
|
# Do not gen and cancel if order is more than 5 minutes old
|
||||||
if order.expires_at < timezone.now():
|
if order.expires_at < timezone.now():
|
||||||
cls.order_expires(order)
|
cls.order_expires(order)
|
||||||
return False, {'Order expired':'cannot generate a bond invoice for an expired order. Make a new one.'}
|
return False, {'bad_request':'Invoice expired. You did not confirm publishing the order in time. Make a new order.'}
|
||||||
|
|
||||||
# Return the previous invoice if there was one
|
# Return the previous invoice if there was one and is still unpaid
|
||||||
if order.maker_bond:
|
if order.maker_bond:
|
||||||
return True, {'invoice':order.maker_bond.invoice,'bond_satoshis':order.maker_bond.num_satoshis}
|
if order.maker_bond.status == LNPayment.Status.INVGEN:
|
||||||
|
return True, {'bond_invoice':order.maker_bond.invoice,'bond_satoshis':order.maker_bond.num_satoshis}
|
||||||
|
else:
|
||||||
|
return False, None
|
||||||
|
|
||||||
order.satoshis_now = cls.satoshis_now(order)
|
order.last_satoshis = cls.satoshis_now(order)
|
||||||
bond_satoshis = int(order.satoshis_now * BOND_SIZE)
|
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
|
||||||
description = f'RoboSats - Publishing {str(order)} - This bond will return to you if you do not cheat.'
|
description = f'RoboSats - Publishing {str(order)} - This bond will return to you if you do not cheat.'
|
||||||
|
|
||||||
# Gen HODL Invoice
|
# Gen HODL Invoice
|
||||||
@ -188,22 +214,27 @@ class Logics():
|
|||||||
expires_at = expires_at)
|
expires_at = expires_at)
|
||||||
|
|
||||||
order.save()
|
order.save()
|
||||||
return True, {'invoice':invoice,'bond_satoshis':bond_satoshis}
|
return True, {'bond_invoice':invoice,'bond_satoshis':bond_satoshis}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def gen_taker_hodl_invoice(cls, order, user):
|
def gen_taker_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 X minutes and unpaid still
|
||||||
if order.taker_bond:
|
if order.taker_bond:
|
||||||
if order.taker_bond.created_at > (timezone.now()+timedelta(minutes=EXP_TAKER_BOND_INVOICE)):
|
# Check if status is INVGEN and still not expired
|
||||||
cls.cancel_order(order, user, 3) # State 3, cancel order before taker bond
|
if order.taker_bond.status == LNPayment.Status.INVGEN:
|
||||||
return False, {'Invoice expired':'You did not confirm taking the order in time.'}
|
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, {'bad_request':'Invoice expired. You did not confirm taking the order in time.'}
|
||||||
|
# Return the previous invoice there was with INVGEN status
|
||||||
|
else:
|
||||||
|
return True, {'bond_invoice':order.taker_bond.invoice,'bond_satoshis':order.taker_bond.num_satoshis}
|
||||||
|
# Invoice exists, but was already locked or settled
|
||||||
else:
|
else:
|
||||||
# Return the previous invoice if there was one
|
return False, None
|
||||||
return True, {'invoice':order.taker_bond.invoice,'bond_satoshis':order.taker_bond.num_satoshis}
|
|
||||||
|
|
||||||
order.satoshis_now = cls.satoshis_now(order)
|
order.last_satoshis = cls.satoshis_now(order) # LOCKS THE AMOUNT OF SATOSHIS FOR THE TRADE
|
||||||
bond_satoshis = int(order.satoshis_now * BOND_SIZE)
|
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
|
||||||
description = f'RoboSats - Taking {str(order)} - This bond will return to you if you do not cheat.'
|
description = f'RoboSats - Taking {str(order)} - This bond will return to you if you do not cheat.'
|
||||||
|
|
||||||
# Gen HODL Invoice
|
# Gen HODL Invoice
|
||||||
@ -222,4 +253,42 @@ class Logics():
|
|||||||
expires_at = expires_at)
|
expires_at = expires_at)
|
||||||
|
|
||||||
order.save()
|
order.save()
|
||||||
return True, {'invoice':invoice,'bond_satoshis': bond_satoshis}
|
return True, {'bond_invoice':invoice,'bond_satoshis': bond_satoshis}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def gen_escrow_hodl_invoice(cls, order, user):
|
||||||
|
|
||||||
|
# Do not generate and cancel if an invoice is there and older than X minutes and unpaid still
|
||||||
|
if order.trade_escrow:
|
||||||
|
# Check if status is INVGEN and still not expired
|
||||||
|
if order.taker_bond.status == LNPayment.Status.INVGEN:
|
||||||
|
if order.taker_bond.created_at > (timezone.now()+timedelta(minutes=EXP_TRADE_ESCR_INVOICE)): # Expired
|
||||||
|
cls.cancel_order(order, user, 4) # State 4, cancel order before trade escrow locked
|
||||||
|
return False, {'bad_request':'Invoice expired. You did not lock the trade escrow in time.'}
|
||||||
|
# Return the previous invoice there was with INVGEN status
|
||||||
|
else:
|
||||||
|
return True, {'escrow_invoice':order.trade_escrow.invoice,'escrow_satoshis':order.trade_escrow.num_satoshis}
|
||||||
|
# Invoice exists, but was already locked or settled
|
||||||
|
else:
|
||||||
|
return False, None # Does not return any context of a healthy locked escrow
|
||||||
|
|
||||||
|
escrow_satoshis = order.last_satoshis # Trade sats amount was fixed at the time of taker bond generation (order.last_satoshis)
|
||||||
|
description = f'RoboSats - Escrow amount for {str(order)} - This escrow will be released to the buyer once you confirm you received the fiat.'
|
||||||
|
|
||||||
|
# Gen HODL Invoice
|
||||||
|
invoice, payment_hash, expires_at = LNNode.gen_hodl_invoice(escrow_satoshis, description, ESCROW_EXPIRY*3600)
|
||||||
|
|
||||||
|
order.taker_bond = LNPayment.objects.create(
|
||||||
|
concept = LNPayment.Concepts.TRESCROW,
|
||||||
|
type = LNPayment.Types.HODL,
|
||||||
|
sender = user,
|
||||||
|
receiver = User.objects.get(username=ESCROW_USERNAME),
|
||||||
|
invoice = invoice,
|
||||||
|
status = LNPayment.Status.INVGEN,
|
||||||
|
num_satoshis = escrow_satoshis,
|
||||||
|
description = description,
|
||||||
|
payment_hash = payment_hash,
|
||||||
|
expires_at = expires_at)
|
||||||
|
|
||||||
|
order.save()
|
||||||
|
return True, {'escrow_invoice':invoice,'escrow_satoshis': escrow_satoshis}
|
@ -27,18 +27,18 @@ class LNPayment(models.Model):
|
|||||||
|
|
||||||
class Concepts(models.IntegerChoices):
|
class Concepts(models.IntegerChoices):
|
||||||
MAKEBOND = 0, 'Maker bond'
|
MAKEBOND = 0, 'Maker bond'
|
||||||
TAKEBOND = 1, 'Taker-buyer bond'
|
TAKEBOND = 1, 'Taker bond'
|
||||||
TRESCROW = 2, 'Trade escrow'
|
TRESCROW = 2, 'Trade escrow'
|
||||||
PAYBUYER = 3, 'Payment to buyer'
|
PAYBUYER = 3, 'Payment to buyer'
|
||||||
|
|
||||||
class Status(models.IntegerChoices):
|
class Status(models.IntegerChoices):
|
||||||
INVGEN = 0, 'Hodl invoice was generated'
|
INVGEN = 0, 'Generated'
|
||||||
LOCKED = 1, 'Hodl invoice has HTLCs locked'
|
LOCKED = 1, 'Locked'
|
||||||
SETLED = 2, 'Invoice settled'
|
SETLED = 2, 'Settled'
|
||||||
RETNED = 3, 'Hodl invoice was returned'
|
RETNED = 3, 'Returned'
|
||||||
MISSNG = 4, 'Buyer invoice is missing'
|
MISSNG = 4, 'Missing'
|
||||||
VALIDI = 5, 'Buyer invoice is valid'
|
VALIDI = 5, 'Valid'
|
||||||
INFAIL = 6, 'Buyer invoice routing failed'
|
INFAIL = 6, 'Failed routing'
|
||||||
|
|
||||||
# payment use details
|
# payment use details
|
||||||
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False, default=Types.HODL)
|
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False, default=Types.HODL)
|
||||||
@ -59,8 +59,7 @@ class LNPayment(models.Model):
|
|||||||
receiver = models.ForeignKey(User, related_name='receiver', on_delete=models.CASCADE, null=True, default=None)
|
receiver = models.ForeignKey(User, related_name='receiver', on_delete=models.CASCADE, null=True, default=None)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# Make relational back to ORDER
|
return (f'HTLC {self.id}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}')
|
||||||
return (f'HTLC {self.id}: {self.Concepts(self.concept).label}')
|
|
||||||
|
|
||||||
class Order(models.Model):
|
class Order(models.Model):
|
||||||
|
|
||||||
@ -74,16 +73,16 @@ class Order(models.Model):
|
|||||||
ETH = 3, 'ETH'
|
ETH = 3, 'ETH'
|
||||||
|
|
||||||
class Status(models.IntegerChoices):
|
class Status(models.IntegerChoices):
|
||||||
WFB = 0, 'Waiting for maker bond'
|
WFB = 0, 'Waiting for maker bond'
|
||||||
PUB = 1, 'Public'
|
PUB = 1, 'Public'
|
||||||
DEL = 2, 'Deleted'
|
DEL = 2, 'Deleted'
|
||||||
TAK = 3, 'Waiting for taker bond'
|
TAK = 3, 'Waiting for taker bond'
|
||||||
UCA = 4, 'Cancelled'
|
UCA = 4, 'Cancelled'
|
||||||
WF2 = 5, 'Waiting for trade collateral and buyer invoice'
|
WF2 = 5, 'Waiting for trade collateral and buyer invoice'
|
||||||
WFE = 6, 'Waiting only for seller trade collateral'
|
WFE = 6, 'Waiting only for seller trade collateral'
|
||||||
WFI = 7, 'Waiting only for buyer invoice'
|
WFI = 7, 'Waiting only for buyer invoice'
|
||||||
CHA = 8, 'Sending fiat - In chatroom'
|
CHA = 8, 'Sending fiat - In chatroom'
|
||||||
CCA = 9, 'Collaboratively cancelled'
|
CCA = 9, 'Collaboratively cancelled'
|
||||||
FSE = 10, 'Fiat sent - In chatroom'
|
FSE = 10, 'Fiat sent - In chatroom'
|
||||||
FCO = 11, 'Fiat confirmed'
|
FCO = 11, 'Fiat confirmed'
|
||||||
SUC = 12, 'Sucessfully settled'
|
SUC = 12, 'Sucessfully settled'
|
||||||
@ -130,7 +129,7 @@ class Order(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# Make relational back to ORDER
|
# Make relational back to ORDER
|
||||||
return (f'Order {self.id}: {self.Types(self.type).label} {"{:,}".format(self.t0_satoshis)} Sats for {self.Currencies(self.currency).label}')
|
return (f'Order {self.id}: {self.Types(self.type).label} BTC for {self.amount} {self.Currencies(self.currency).label}')
|
||||||
|
|
||||||
@receiver(pre_delete, sender=Order)
|
@receiver(pre_delete, sender=Order)
|
||||||
def delelete_HTLCs_at_order_deletion(sender, instance, **kwargs):
|
def delelete_HTLCs_at_order_deletion(sender, instance, **kwargs):
|
||||||
|
136
api/views.py
136
api/views.py
@ -7,7 +7,7 @@ from django.contrib.auth import authenticate, login, logout
|
|||||||
from django.contrib.auth.models import User
|
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 LNPayment, Order
|
||||||
from .logics import Logics
|
from .logics import Logics
|
||||||
|
|
||||||
from .nick_generator.nick_generator import NickGenerator
|
from .nick_generator.nick_generator import NickGenerator
|
||||||
@ -74,75 +74,100 @@ class OrderView(viewsets.ViewSet):
|
|||||||
lookup_url_kwarg = 'order_id'
|
lookup_url_kwarg = 'order_id'
|
||||||
|
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
|
'''
|
||||||
|
Full trade pipeline takes place while looking/refreshing the order page.
|
||||||
|
'''
|
||||||
order_id = request.GET.get(self.lookup_url_kwarg)
|
order_id = request.GET.get(self.lookup_url_kwarg)
|
||||||
|
|
||||||
if order_id == None:
|
if order_id == None:
|
||||||
return Response({'Bad Request':'Order ID parameter not found in request'}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'bad_request':'Order ID parameter not found in request'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
order = Order.objects.filter(id=order_id)
|
order = Order.objects.filter(id=order_id)
|
||||||
|
|
||||||
# check if exactly one order is found in the db
|
# check if exactly one order is found in the db
|
||||||
if len(order) == 1 :
|
if len(order) != 1 :
|
||||||
order = order[0]
|
return Response({'bad_request':'Invalid Order Id'}, status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
# 1) If order expired
|
# This is our order.
|
||||||
if order.status == Order.Status.EXP:
|
order = order[0]
|
||||||
return Response({'bad_request':'This order has expired'},status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# 2) If order cancelled
|
# 1) If order expired
|
||||||
if order.status == Order.Status.UCA:
|
if order.status == Order.Status.EXP:
|
||||||
return Response({'bad_request':'This order has been cancelled by the maker'},status.HTTP_400_BAD_REQUEST)
|
return Response({'bad_request':'This order has expired'},status.HTTP_400_BAD_REQUEST)
|
||||||
if order.status == Order.Status.CCA:
|
|
||||||
return Response({'bad_request':'This order has been cancelled collaborativelly'},status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
data = ListOrderSerializer(order).data
|
# 2) If order cancelled
|
||||||
|
if order.status == Order.Status.UCA:
|
||||||
|
return Response({'bad_request':'This order has been cancelled by the maker'},status.HTTP_400_BAD_REQUEST)
|
||||||
|
if order.status == Order.Status.CCA:
|
||||||
|
return Response({'bad_request':'This order has been cancelled collaborativelly'},status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# Add booleans if user is maker, taker, partipant, buyer or seller
|
data = ListOrderSerializer(order).data
|
||||||
data['is_maker'] = order.maker == request.user
|
|
||||||
data['is_taker'] = order.taker == request.user
|
|
||||||
data['is_participant'] = data['is_maker'] or data['is_taker']
|
|
||||||
|
|
||||||
# 3) If not a participant and order is not public, forbid.
|
# Add booleans if user is maker, taker, partipant, buyer or seller
|
||||||
if not data['is_participant'] and order.status != Order.Status.PUB:
|
data['is_maker'] = order.maker == request.user
|
||||||
return Response({'bad_request':'Not allowed to see this order'},status.HTTP_403_FORBIDDEN)
|
data['is_taker'] = order.taker == request.user
|
||||||
|
data['is_participant'] = data['is_maker'] or data['is_taker']
|
||||||
|
|
||||||
# 4) Non participants can view details (but only if PUB)
|
# 3) If not a participant and order is not public, forbid.
|
||||||
elif not data['is_participant'] and order.status != Order.Status.PUB:
|
if not data['is_participant'] and order.status != Order.Status.PUB:
|
||||||
return Response(data, status=status.HTTP_200_OK)
|
return Response({'bad_request':'You are not allowed to see this order'},status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
# For participants add position side, nicks and status as message
|
# 4) Non participants can view details (but only if PUB)
|
||||||
data['is_buyer'] = Logics.is_buyer(order,request.user)
|
elif not data['is_participant'] and order.status != Order.Status.PUB:
|
||||||
data['is_seller'] = Logics.is_seller(order,request.user)
|
return Response(data, status=status.HTTP_200_OK)
|
||||||
data['maker_nick'] = str(order.maker)
|
|
||||||
data['taker_nick'] = str(order.taker)
|
|
||||||
data['status_message'] = Order.Status(order.status).label
|
|
||||||
|
|
||||||
# 5) If status is 'waiting for maker bond' and user is MAKER, reply with a MAKER HODL invoice.
|
# For participants add position side, nicks and status as message
|
||||||
if order.status == Order.Status.WFB and data['is_maker']:
|
data['is_buyer'] = Logics.is_buyer(order,request.user)
|
||||||
valid, context = Logics.gen_maker_hodl_invoice(order, request.user)
|
data['is_seller'] = Logics.is_seller(order,request.user)
|
||||||
if valid:
|
data['maker_nick'] = str(order.maker)
|
||||||
data = {**data, **context}
|
data['taker_nick'] = str(order.taker)
|
||||||
else:
|
data['status_message'] = Order.Status(order.status).label
|
||||||
return Response(context, status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# 6) If status is 'waiting for taker bond' and user is TAKER, reply with a TAKER HODL invoice.
|
# 5) If status is 'waiting for maker bond' and user is MAKER, reply with a MAKER HODL invoice.
|
||||||
elif order.status == Order.Status.TAK and data['is_taker']:
|
if order.status == Order.Status.WFB and data['is_maker']:
|
||||||
valid, context = Logics.gen_taker_hodl_invoice(order, request.user)
|
valid, context = Logics.gen_maker_hodl_invoice(order, request.user)
|
||||||
if valid:
|
if valid:
|
||||||
data = {**data, **context}
|
data = {**data, **context}
|
||||||
else:
|
else:
|
||||||
return Response(context, status.HTTP_400_BAD_REQUEST)
|
return Response(context, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# 7) If status is 'WF2'or'WTC' and user is Seller, reply with an ESCROW HODL invoice.
|
# 6) If status is 'waiting for taker bond' and user is TAKER, reply with a TAKER HODL invoice.
|
||||||
elif (order.status == Order.Status.WF2 or order.status == Order.Status.WFE) and data['is_seller']:
|
elif order.status == Order.Status.TAK and data['is_taker']:
|
||||||
valid, context = Logics.gen_seller_hodl_invoice(order, request.user)
|
valid, context = Logics.gen_taker_hodl_invoice(order, request.user)
|
||||||
if valid:
|
if valid:
|
||||||
data = {**data, **context}
|
data = {**data, **context}
|
||||||
else:
|
else:
|
||||||
return Response(context, status.HTTP_400_BAD_REQUEST)
|
return Response(context, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
return Response(data, status.HTTP_200_OK)
|
# 7) If status is 'WF2'or'WTC'
|
||||||
return Response({'Order Not Found':'Invalid Order Id'}, status.HTTP_404_NOT_FOUND)
|
elif (order.status == Order.Status.WF2 or order.status == Order.Status.WFE):
|
||||||
|
|
||||||
|
# If the two bonds are locked
|
||||||
|
if order.maker_bond.status == order.taker_bond.status == LNPayment.Status.LOCKED:
|
||||||
|
|
||||||
|
# 7.a) And if user is Seller, reply with an ESCROW HODL invoice.
|
||||||
|
if data['is_seller']:
|
||||||
|
valid, context = Logics.gen_escrow_hodl_invoice(order, request.user)
|
||||||
|
if valid:
|
||||||
|
data = {**data, **context}
|
||||||
|
else:
|
||||||
|
return Response(context, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
# 7.b) If user is Buyer, reply with an AMOUNT so he can send the buyer invoice.
|
||||||
|
elif data['is_buyer']:
|
||||||
|
valid, context = Logics.buyer_invoice_amount(order, request.user)
|
||||||
|
if valid:
|
||||||
|
data = {**data, **context}
|
||||||
|
else:
|
||||||
|
return Response(context, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
# 8) If status is 'CHA'or '' or '' and all HTLCS are in LOCKED
|
||||||
|
elif order.status == Order.Status.CHA: # TODO Add the other status
|
||||||
|
if order.maker_bond.status == order.taker_bond.status == order.trade_escrow.status == LNPayment.Status.LOCKED:
|
||||||
|
# add whether a collaborative cancel is pending
|
||||||
|
data['pending_cancel'] = order.is_pending_cancel
|
||||||
|
|
||||||
|
return Response(data, status.HTTP_200_OK)
|
||||||
|
|
||||||
def take_update_confirm_dispute_cancel(self, request, format=None):
|
def take_update_confirm_dispute_cancel(self, request, format=None):
|
||||||
'''
|
'''
|
||||||
@ -177,7 +202,7 @@ class OrderView(viewsets.ViewSet):
|
|||||||
|
|
||||||
# 3) If action is cancel
|
# 3) If action is cancel
|
||||||
elif action == 'cancel':
|
elif action == 'cancel':
|
||||||
valid, context = Logics.cancel_order(order,request.user,invoice)
|
valid, context = Logics.cancel_order(order,request.user)
|
||||||
if not valid: return Response(context,status.HTTP_400_BAD_REQUEST)
|
if not valid: return Response(context,status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# 4) If action is confirm
|
# 4) If action is confirm
|
||||||
@ -190,11 +215,12 @@ class OrderView(viewsets.ViewSet):
|
|||||||
|
|
||||||
# 6) If action is dispute
|
# 6) If action is dispute
|
||||||
elif action == 'rate' and rating:
|
elif action == 'rate' and rating:
|
||||||
pass
|
valid, context = Logics.rate_counterparty(order,request.user, rating)
|
||||||
|
if not valid: return Response(context,status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# If nothing... something else is going on. Probably not allowed!
|
# If nothing... something else is going on. Probably not allowed!
|
||||||
else:
|
else:
|
||||||
return Response({'bad_request':'Not allowed'})
|
return Response({'bad_request':'The Robotic Satoshis working in the warehouse did not understand you'})
|
||||||
|
|
||||||
return self.get(request)
|
return self.get(request)
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Paper, Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup, Menu} from "@material-ui/core"
|
import { Paper, Alert, AlertTitle, Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup, Menu} from "@material-ui/core"
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
@ -104,7 +104,8 @@ export default class MakerPage extends Component {
|
|||||||
};
|
};
|
||||||
fetch("/api/make/",requestOptions)
|
fetch("/api/make/",requestOptions)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => (console.log(data) & this.props.history.push('/order/' + data.id)));
|
.then((data) => (this.setState({badRequest:data.bad_request})
|
||||||
|
& (data.id ? this.props.history.push('/order/' + data.id) :"")));
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -242,6 +243,13 @@ export default class MakerPage extends Component {
|
|||||||
<Button color="primary" variant="contained" onClick={this.handleCreateOfferButtonPressed} >
|
<Button color="primary" variant="contained" onClick={this.handleCreateOfferButtonPressed} >
|
||||||
Create Order
|
Create Order
|
||||||
</Button>
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
{this.state.badRequest ?
|
||||||
|
<Typography component="subtitle2" variant="subtitle2" color="secondary">
|
||||||
|
{this.state.badRequest} <br/>
|
||||||
|
</Typography>
|
||||||
|
: ""}
|
||||||
<Typography component="subtitle2" variant="subtitle2">
|
<Typography component="subtitle2" variant="subtitle2">
|
||||||
<div align='center'>
|
<div align='center'>
|
||||||
Create a BTC {this.state.type==0 ? "buy":"sell"} order for {this.state.amount} {this.state.currencyCode}
|
Create a BTC {this.state.type==0 ? "buy":"sell"} order for {this.state.amount} {this.state.currencyCode}
|
||||||
|
@ -68,6 +68,7 @@ export default class OrderPage extends Component {
|
|||||||
isBuyer:data.buyer,
|
isBuyer:data.buyer,
|
||||||
isSeller:data.seller,
|
isSeller:data.seller,
|
||||||
expiresAt:data.expires_at,
|
expiresAt:data.expires_at,
|
||||||
|
badRequest:data.bad_request,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user