Add onchain logics pt2

This commit is contained in:
Reckless_Satoshi 2022-06-06 13:37:51 -07:00
parent 8d0b518222
commit cf82a4d6ae
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
7 changed files with 83 additions and 15 deletions

View File

@ -113,9 +113,9 @@ MAX_SWAP_FEE = 0.1
# Liquidity split point (LN/onchain) at which we use MAX_SWAP_FEE # Liquidity split point (LN/onchain) at which we use MAX_SWAP_FEE
MAX_SWAP_POINT = 0 MAX_SWAP_POINT = 0
# Shape of fee to available liquidity curve. Only 'linear' implemented. # Shape of fee to available liquidity curve. Only 'linear' implemented.
SWAP_FEE_ = 'linear' SWAP_FEE_SHAPE = 'linear'
# Min amount allowed for Swap # Min amount allowed for Swap
MIN_SWAP_AMOUNT = 800000 MIN_SWAP_AMOUNT = 50000
# Reward tip. Reward for every finished trade in the referral program (Satoshis) # Reward tip. Reward for every finished trade in the referral program (Satoshis)
REWARD_TIP = 100 REWARD_TIP = 100

View File

@ -305,7 +305,7 @@ class LNNode:
lnpayment.num_satoshis * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")), lnpayment.num_satoshis * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")),
float(config("MIN_FLAT_ROUTING_FEE_LIMIT_REWARD")), float(config("MIN_FLAT_ROUTING_FEE_LIMIT_REWARD")),
)) # 200 ppm or 10 sats )) # 200 ppm or 10 sats
timeout_seconds = int(config("PAYOUT_TIMEOUT_SECONDS")) timeout_seconds = int(config("REWARDS_TIMEOUT_SECONDS"))
request = routerrpc.SendPaymentRequest(payment_request=lnpayment.invoice, request = routerrpc.SendPaymentRequest(payment_request=lnpayment.invoice,
fee_limit_sat=fee_limit_sat, fee_limit_sat=fee_limit_sat,
timeout_seconds=timeout_seconds) timeout_seconds=timeout_seconds)

View File

@ -4,7 +4,7 @@ from django.utils import timezone
from api.lightning.node import LNNode from api.lightning.node import LNNode
from django.db.models import Q from django.db.models import Q
from api.models import Order, LNPayment, MarketTick, User, Currency from api.models import OnchainPayment, Order, LNPayment, MarketTick, User, Currency
from api.tasks import send_message from api.tasks import send_message
from decouple import config from decouple import config
@ -494,10 +494,46 @@ class Logics:
order.save() order.save()
return True, None return True, None
def compute_swap_fee_rate(balance):
shape = str(config('SWAP_FEE_SHAPE'))
if shape == "linear":
MIN_SWAP_FEE = float(config('MIN_SWAP_FEE'))
MIN_POINT = float(config('MIN_POINT'))
MAX_SWAP_FEE = float(config('MAX_SWAP_FEE'))
MAX_POINT = float(config('MAX_POINT'))
if balance.onchain_fraction > MIN_POINT:
swap_fee_rate = MIN_SWAP_FEE
else:
slope = (MAX_SWAP_FEE - MIN_SWAP_FEE) / (MAX_POINT - MIN_POINT)
swap_fee_rate = slope * (balance.onchain_fraction - MAX_POINT) + MAX_SWAP_FEE
return swap_fee_rate
@classmethod
def create_onchain_payment(cls, order, estimate_sats):
'''
Creates an empty OnchainPayment for order.payout_tx.
It sets the fees to be applied to this order if onchain Swap is used.
If the user submits a LN invoice instead. The returned OnchainPayment goes unused.
'''
onchain_payment = OnchainPayment.objects.create()
onchain_payment.suggested_mining_fee_rate = LNNode.estimate_fee(amount_sats=estimate_sats)
onchain_payment.swap_fee_rate = cls.compute_swap_fee_rate(onchain_payment.balance)
onchain_payment.save()
order.payout_tx = onchain_payment
order.save()
return True, None
@classmethod @classmethod
def payout_amount(cls, order, user): def payout_amount(cls, order, user):
"""Computes buyer invoice amount. Uses order.last_satoshis, """Computes buyer invoice amount. Uses order.last_satoshis,
that is the final trade amount set at Taker Bond time""" that is the final trade amount set at Taker Bond time
Adds context for onchain swap.
"""
if not cls.is_buyer(order, user):
return False, None
if user == order.maker: if user == order.maker:
fee_fraction = FEE * MAKER_FEE_SPLIT fee_fraction = FEE * MAKER_FEE_SPLIT
@ -508,10 +544,25 @@ class Logics:
reward_tip = int(config('REWARD_TIP')) if user.profile.is_referred else 0 reward_tip = int(config('REWARD_TIP')) if user.profile.is_referred else 0
if cls.is_buyer(order, user): context = {}
invoice_amount = round(order.last_satoshis - fee_sats - reward_tip) # Trading fee to buyer is charged here. # context necessary for the user to submit a LN invoice
context["invoice_amount"] = round(order.last_satoshis - fee_sats - reward_tip) # Trading fee to buyer is charged here.
return True, {"invoice_amount": invoice_amount} # context necessary for the user to submit an onchain address
MIN_SWAP_AMOUNT = int(config("MIN_SWAP_AMOUNT"))
if context["invoice_amount"] < MIN_SWAP_AMOUNT:
context["swap_allowed"] = False
return True, context
if order.payout_tx == None:
cls.create_onchain_payment(order, estimate_sats=context["invoice_amount"])
context["swap_allowed"] = True
context["suggested_mining_fee_rate"] = order.payout_tx.suggested_mining_fee_rate
context["swap_fee_rate"] = order.payout_tx.swap_fee_rate
return True, context
@classmethod @classmethod
def escrow_amount(cls, order, user): def escrow_amount(cls, order, user):

View File

@ -219,10 +219,11 @@ class OnchainPayment(models.Model):
blank=False) blank=False)
# platform onchain/channels balance at creattion, swap fee rate as percent of total volume # platform onchain/channels balance at creattion, swap fee rate as percent of total volume
node_balance = models.ForeignKey(BalanceLog, balance = models.ForeignKey(BalanceLog,
related_name="balance", related_name="balance",
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True) default=BalanceLog.objects.create)
swap_fee_rate = models.DecimalField(max_digits=4, swap_fee_rate = models.DecimalField(max_digits=4,
decimal_places=2, decimal_places=2,
default=2, default=2,
@ -452,7 +453,16 @@ class Order(models.Model):
# buyer payment LN invoice # buyer payment LN invoice
payout = models.OneToOneField( payout = models.OneToOneField(
LNPayment, LNPayment,
related_name="order_paid", related_name="order_paid_LN",
on_delete=models.SET_NULL,
null=True,
default=None,
blank=True,
)
payout_tx = models.OneToOneField(
OnchainPayment,
related_name="order_paid_TX",
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
default=None, default=None,

View File

@ -54,6 +54,10 @@ class UpdateOrderSerializer(serializers.Serializer):
allow_null=True, allow_null=True,
allow_blank=True, allow_blank=True,
default=None) default=None)
address = serializers.CharField(max_length=100,
allow_null=True,
allow_blank=True,
default=None)
statement = serializers.CharField(max_length=10000, statement = serializers.CharField(max_length=10000,
allow_null=True, allow_null=True,
allow_blank=True, allow_blank=True,
@ -63,6 +67,7 @@ class UpdateOrderSerializer(serializers.Serializer):
"pause", "pause",
"take", "take",
"update_invoice", "update_invoice",
"update_address",
"submit_statement", "submit_statement",
"dispute", "dispute",
"cancel", "cancel",
@ -79,6 +84,7 @@ class UpdateOrderSerializer(serializers.Serializer):
default=None, default=None,
) )
amount = serializers.DecimalField(max_digits=18, decimal_places=8, allow_null=True, required=False, default=None) amount = serializers.DecimalField(max_digits=18, decimal_places=8, allow_null=True, required=False, default=None)
mining_fee_rate = serializers.DecimalField(max_digits=6, decimal_places=3, allow_null=True, required=False, default=None)
class UserGenSerializer(serializers.Serializer): class UserGenSerializer(serializers.Serializer):
# Mandatory fields # Mandatory fields

View File

@ -79,7 +79,7 @@ def follow_send_payment(hash):
float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")), float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")),
float(config("MIN_FLAT_ROUTING_FEE_LIMIT")), float(config("MIN_FLAT_ROUTING_FEE_LIMIT")),
)) # 1000 ppm or 10 sats )) # 1000 ppm or 10 sats
timeout_seconds = int(config("REWARDS_TIMEOUT_SECONDS")) timeout_seconds = int(config("PAYOUT_TIMEOUT_SECONDS"))
request = LNNode.routerrpc.SendPaymentRequest( request = LNNode.routerrpc.SendPaymentRequest(
payment_request=lnpayment.invoice, payment_request=lnpayment.invoice,

View File

@ -420,6 +420,7 @@ class OrderView(viewsets.ViewSet):
action = serializer.data.get("action") action = serializer.data.get("action")
invoice = serializer.data.get("invoice") invoice = serializer.data.get("invoice")
address = serializer.data.get("address") address = serializer.data.get("address")
mining_fee_rate = serializer.data.get("mining_fee_rate")
statement = serializer.data.get("statement") statement = serializer.data.get("statement")
rating = serializer.data.get("rating") rating = serializer.data.get("rating")
@ -469,7 +470,7 @@ class OrderView(viewsets.ViewSet):
# 2.b) If action is 'update invoice' # 2.b) If action is 'update invoice'
if action == "update_address": if action == "update_address":
valid, context = Logics.update_address(order, request.user, valid, context = Logics.update_address(order, request.user,
address) address, mining_fee_rate)
if not valid: if not valid:
return Response(context, status.HTTP_400_BAD_REQUEST) return Response(context, status.HTTP_400_BAD_REQUEST)