mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 12:11:35 +00:00
Add automatic dispute resolution (#437)
* Add automatic dispute resolution logic * Small fixes
This commit is contained in:
parent
b399d6a608
commit
7e47fb60bf
@ -11,6 +11,7 @@ from api.lightning.node import LNNode
|
||||
from api.models import Currency, LNPayment, MarketTick, OnchainPayment, Order, User
|
||||
from api.tasks import send_notification
|
||||
from api.utils import validate_onchain_address
|
||||
from chat.models import Message
|
||||
|
||||
FEE = float(config("FEE"))
|
||||
MAKER_FEE_SPLIT = float(config("MAKER_FEE_SPLIT"))
|
||||
@ -428,6 +429,65 @@ class Logics:
|
||||
cls.publish_order(order)
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def automatic_dispute_resolution(cls, order):
|
||||
"""Simple case where a dispute can be solved with a
|
||||
priori knowledge. For example, a dispute that opens
|
||||
at expiration on an order where one of the participants
|
||||
never sent a message on the chat and never marked 'fiat
|
||||
sent'. By solving the dispute automatically before
|
||||
flagging it as dispute, we avoid having to settle the
|
||||
bonds"""
|
||||
|
||||
# If fiat has been marked as sent, automatic dispute
|
||||
# resolution is not possible.
|
||||
if order.is_fiat_sent:
|
||||
return False
|
||||
|
||||
# If the order has not entered dispute due to time expire
|
||||
# (a user triggered it), automatic dispute resolution is
|
||||
# not possible.
|
||||
if order.expires_at >= timezone.now():
|
||||
return False
|
||||
|
||||
num_messages_taker = len(
|
||||
Message.objects.filter(order=order, sender=order.taker)
|
||||
)
|
||||
num_messages_maker = len(
|
||||
Message.objects.filter(order=order, sender=order.maker)
|
||||
)
|
||||
|
||||
if num_messages_maker == num_messages_taker == 0:
|
||||
cls.return_escrow(order)
|
||||
cls.settle_bond(order.maker_bond)
|
||||
cls.settle_bond(order.taker_bond)
|
||||
order.status = Order.Status.DIS
|
||||
|
||||
elif num_messages_maker == 0:
|
||||
cls.return_escrow(order)
|
||||
cls.settle_bond(order.maker_bond)
|
||||
cls.return_bond(order.taker_bond)
|
||||
cls.add_slashed_rewards(order.maker_bond, order.taker.profile)
|
||||
order.status = Order.Status.MLD
|
||||
|
||||
elif num_messages_maker == 0:
|
||||
cls.return_escrow(order)
|
||||
cls.settle_bond(order.maker_bond)
|
||||
cls.return_bond(order.taker_bond)
|
||||
cls.add_slashed_rewards(order.taker_bond, order.maker.profile)
|
||||
order.status = Order.Status.TLD
|
||||
else:
|
||||
return False
|
||||
|
||||
order.is_disputed = True
|
||||
order.expires_at = timezone.now() + timedelta(
|
||||
seconds=order.t_to_expire(Order.Status.DIS)
|
||||
)
|
||||
order.save()
|
||||
send_notification.delay(order_id=order.id, message="dispute_opened")
|
||||
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def open_dispute(cls, order, user=None):
|
||||
|
||||
@ -446,6 +506,11 @@ class Logics:
|
||||
"bad_request": "You cannot open a dispute of this order at this stage"
|
||||
}
|
||||
|
||||
automatically_solved = cls.automatic_dispute_resolution(order)
|
||||
|
||||
if automatically_solved:
|
||||
return True, None
|
||||
|
||||
if not order.trade_escrow.status == LNPayment.Status.SETLED:
|
||||
cls.settle_escrow(order)
|
||||
cls.settle_bond(order.maker_bond)
|
||||
@ -481,9 +546,9 @@ class Logics:
|
||||
"bad_request": "Only orders in dispute accept dispute statements"
|
||||
}
|
||||
|
||||
if len(statement) > 10000:
|
||||
if len(statement) > 50000:
|
||||
return False, {
|
||||
"bad_statement": "The statement is longer than 10000 characters"
|
||||
"bad_statement": "The statement and chatlogs are longer than 50000 characters"
|
||||
}
|
||||
|
||||
if len(statement) < 100:
|
||||
@ -1601,7 +1666,6 @@ class Logics:
|
||||
def add_slashed_rewards(cls, bond, profile):
|
||||
"""
|
||||
When a bond is slashed due to overtime, rewards the user that was waiting.
|
||||
If participants of the order were referred, the reward is given to the referees.
|
||||
"""
|
||||
reward_fraction = float(config("SLASHED_BOND_REWARD_SPLIT"))
|
||||
reward = int(bond.num_satoshis * reward_fraction)
|
||||
|
@ -444,10 +444,10 @@ class Order(models.Model):
|
||||
# in dispute
|
||||
is_disputed = models.BooleanField(default=False, null=False)
|
||||
maker_statement = models.TextField(
|
||||
max_length=10000, null=True, default=None, blank=True
|
||||
max_length=50000, null=True, default=None, blank=True
|
||||
)
|
||||
taker_statement = models.TextField(
|
||||
max_length=10000, null=True, default=None, blank=True
|
||||
max_length=50000, null=True, default=None, blank=True
|
||||
)
|
||||
|
||||
# LNpayments
|
||||
|
@ -492,7 +492,7 @@ class UpdateOrderSerializer(serializers.Serializer):
|
||||
routing_budget_ppm = serializers.IntegerField(
|
||||
default=0,
|
||||
min_value=0,
|
||||
max_value=100000,
|
||||
max_value=100001,
|
||||
allow_null=True,
|
||||
required=False,
|
||||
help_text="Max budget to allocate for routing in PPM",
|
||||
@ -501,7 +501,7 @@ class UpdateOrderSerializer(serializers.Serializer):
|
||||
max_length=100, allow_null=True, allow_blank=True, default=None
|
||||
)
|
||||
statement = serializers.CharField(
|
||||
max_length=11000, allow_null=True, allow_blank=True, default=None
|
||||
max_length=500000, allow_null=True, allow_blank=True, default=None
|
||||
)
|
||||
action = serializers.ChoiceField(
|
||||
choices=(
|
||||
|
Loading…
Reference in New Issue
Block a user