Add cancel taker penalty time

This commit is contained in:
Reckless_Satoshi 2022-01-10 04:10:32 -08:00
parent cf11583d6d
commit de66040893
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
6 changed files with 67 additions and 10 deletions

View File

@ -5,6 +5,8 @@ MARKET_PRICE_API = 'https://blockchain.info/ticker'
FEE = 0.002 FEE = 0.002
# Bond size in percentage % # Bond size in percentage %
BOND_SIZE = 0.01 BOND_SIZE = 0.01
# Time out penalty for canceling takers in MINUTES
PENALTY_TIMEOUT = 2
# Trade limits in satoshis # Trade limits in satoshis
MIN_TRADE = 10000 MIN_TRADE = 10000

View File

@ -10,6 +10,7 @@ FEE = float(config('FEE'))
BOND_SIZE = float(config('BOND_SIZE')) 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')
PENALTY_TIMEOUT = int(config('PENALTY_TIMEOUT'))
MIN_TRADE = int(config('MIN_TRADE')) MIN_TRADE = int(config('MIN_TRADE'))
MAX_TRADE = int(config('MAX_TRADE')) MAX_TRADE = int(config('MAX_TRADE'))
@ -21,6 +22,7 @@ EXP_TRADE_ESCR_INVOICE = int(config('EXP_TRADE_ESCR_INVOICE'))
BOND_EXPIRY = int(config('BOND_EXPIRY')) BOND_EXPIRY = int(config('BOND_EXPIRY'))
ESCROW_EXPIRY = int(config('ESCROW_EXPIRY')) ESCROW_EXPIRY = int(config('ESCROW_EXPIRY'))
class Logics(): class Logics():
def validate_already_maker_or_taker(user): def validate_already_maker_or_taker(user):
@ -41,10 +43,16 @@ class Logics():
return False, {'bad_request': 'Your order is too small. It is worth '+'{:,}'.format(order.t0_satoshis)+' Sats now. But limit is '+'{:,}'.format(MIN_TRADE)+ ' Sats'} return False, {'bad_request': 'Your order is too small. It is worth '+'{:,}'.format(order.t0_satoshis)+' Sats now. But limit is '+'{:,}'.format(MIN_TRADE)+ ' Sats'}
return True, None return True, None
def take(order, user): @classmethod
order.taker = user def take(cls, order, user):
order.status = Order.Status.TAK is_penalized, time_out = cls.is_penalized(user)
order.save() if is_penalized:
return False, {'bad_request',f'You need to wait {time_out} seconds to take an order'}
else:
order.taker = user
order.status = Order.Status.TAK
order.save()
return True, None
def is_buyer(order, user): def is_buyer(order, user):
is_maker = order.maker == user is_maker = order.maker == user
@ -167,6 +175,18 @@ class Logics():
order.save() order.save()
return True, None return True, None
def is_penalized(user):
''' Checks if a user that is not participant of orders
has a limit on taking or making a order'''
if user.profile.penalty_expiration:
if user.profile.penalty_expiration > timezone.now():
time_out = (user.profile.penalty_expiration - timezone.now()).seconds
return True, time_out
return False, None
@classmethod @classmethod
def cancel_order(cls, order, user, state=None): def cancel_order(cls, order, user, state=None):
@ -181,12 +201,24 @@ class Logics():
# 2) When maker cancels after bond # 2) When maker cancels after bond
'''The order dissapears from book and goes to cancelled. '''The order dissapears from book and goes to cancelled.
Maker is charged a small amount of sats, to prevent DDOS Maker is charged the bond to prevent DDOS
on the LN node and order book''' on the LN node and order book. TODO Only charge a small part
of the bond (requires maker submitting an invoice)'''
# 3) When taker cancels before bond # 3) When taker cancels before bond
''' 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() '''
elif order.status == Order.Status.TAK and order.taker == user:
# adds a timeout penalty
user.profile.penalty_expiration = timezone.now() + timedelta(seconds=PENALTY_TIMEOUT)
user.save()
order.taker = None
order.status = Order.Status.PUB
order.save()
return True, None
# 4) When taker or maker cancel after bond (before escrow) # 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.

View File

@ -158,6 +158,9 @@ class Profile(models.Model):
# RoboHash # RoboHash
avatar = models.ImageField(default="static/assets/misc/unknown_avatar.png", verbose_name='Avatar', blank=True) avatar = models.ImageField(default="static/assets/misc/unknown_avatar.png", verbose_name='Avatar', blank=True)
# Penalty expiration (only used then taking/cancelling repeatedly orders in the book before comitting bond)
penalty_expiration = models.DateTimeField(null=True)
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs): def create_user_profile(sender, instance, created, **kwargs):
if created: if created:

View File

@ -5,7 +5,7 @@ import ring
storage = {} storage = {}
@ring.dict(storage, expire=10) #keeps in cache for 10 seconds @ring.dict(storage, expire=30) #keeps in cache for 30 seconds
def get_exchange_rate(currency): def get_exchange_rate(currency):
# 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) # Think about polling price data in a different way (e.g. store locally every t seconds)

View File

@ -105,6 +105,11 @@ class OrderView(viewsets.ViewSet):
data = ListOrderSerializer(order).data data = ListOrderSerializer(order).data
# if user is under a limit (penalty), inform him
is_penalized, time_out = Logics.is_penalized(request.user)
if is_penalized:
data['penalty'] = time_out
# Add booleans if user is maker, taker, partipant, buyer or seller # Add booleans if user is maker, taker, partipant, buyer or seller
data['is_maker'] = order.maker == request.user data['is_maker'] = order.maker == request.user
data['is_taker'] = order.taker == request.user data['is_taker'] = order.taker == request.user
@ -207,7 +212,9 @@ class OrderView(viewsets.ViewSet):
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=status.HTTP_409_CONFLICT)
Logics.take(order, request.user) valid, context = Logics.take(order, request.user)
if not valid: return Response(context, status=status.HTTP_403_FORBIDDEN)
else: Response({'bad_request':'This order is not public anymore.'}, status.HTTP_400_BAD_REQUEST) else: Response({'bad_request':'This order is not public anymore.'}, status.HTTP_400_BAD_REQUEST)
# Any other action is only allowed if the user is a participant # Any other action is only allowed if the user is a participant

View File

@ -1,5 +1,5 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Paper, CircularProgress, Button , Grid, Typography, List, ListItem, ListItemText, ListItemAvatar, Avatar, Divider, Box, LinearProgress} from "@mui/material" import { Alert, Paper, CircularProgress, Button , Grid, Typography, List, ListItem, ListItemText, ListItemAvatar, Avatar, Divider, Box, LinearProgress} from "@mui/material"
import TradeBox from "./TradeBox"; import TradeBox from "./TradeBox";
function msToTime(duration) { function msToTime(duration) {
@ -101,6 +101,7 @@ export default class OrderPage extends Component {
isTaker: data.is_taker, isTaker: data.is_taker,
isBuyer: data.is_buyer, isBuyer: data.is_buyer,
isSeller: data.is_seller, isSeller: data.is_seller,
penalty: data.penalty,
expiresAt: data.expires_at, expiresAt: data.expires_at,
badRequest: data.bad_request, badRequest: data.bad_request,
bondInvoice: data.bond_invoice, bondInvoice: data.bond_invoice,
@ -250,6 +251,18 @@ export default class OrderPage extends Component {
<LinearDeterminate /> <LinearDeterminate />
</List> </List>
{/* If the user has a penalty/limit */}
{this.state.penalty ?
<>
<Divider />
<Grid item xs={12} align="center">
<Alert severity="warning" sx={{maxWidth:360}}>
You cannot take an order yet! Wait {this.state.penalty} seconds
</Alert>
</Grid>
</>
: null}
</Paper> </Paper>
</Grid> </Grid>