diff --git a/api/lightning/cln.py b/api/lightning/cln.py index b005d4ece..da7ef21e3 100755 --- a/api/lightning/cln.py +++ b/api/lightning/cln.py @@ -494,13 +494,15 @@ def pay_invoice(cls, lnpayment): """Sends sats. Used for rewards payouts""" from api.models import LNPayment - fee_limit_sat = int( - max( - lnpayment.num_satoshis - * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")), - float(config("MIN_FLAT_ROUTING_FEE_LIMIT_REWARD")), + fee_limit_sat = int(lnpayment.routing_budget_sats) + if fee_limit_sat == 0 and lnpayment.routing_budget_ppm == 0: + fee_limit_sat = int( + max( + lnpayment.num_satoshis + * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")), + float(config("MIN_FLAT_ROUTING_FEE_LIMIT_REWARD")), + ) ) - ) # 1000 ppm or 2 sats timeout_seconds = int(config("REWARDS_TIMEOUT_SECONDS")) request = node_pb2.PayRequest( bolt11=lnpayment.invoice, diff --git a/api/lightning/lnd.py b/api/lightning/lnd.py index e43e11b7d..7afee83e2 100644 --- a/api/lightning/lnd.py +++ b/api/lightning/lnd.py @@ -466,13 +466,15 @@ def pay_invoice(cls, lnpayment): """Sends sats. Used for rewards payouts""" from api.models import LNPayment - fee_limit_sat = int( - max( - lnpayment.num_satoshis - * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")), - float(config("MIN_FLAT_ROUTING_FEE_LIMIT_REWARD")), + fee_limit_sat = int(lnpayment.routing_budget_sats) + if fee_limit_sat == 0 and lnpayment.routing_budget_ppm == 0: + fee_limit_sat = int( + max( + lnpayment.num_satoshis + * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")), + float(config("MIN_FLAT_ROUTING_FEE_LIMIT_REWARD")), + ) ) - ) # 1000 ppm or 2 sats timeout_seconds = int(config("REWARDS_TIMEOUT_SECONDS")) request = router_pb2.SendPaymentRequest( payment_request=lnpayment.invoice, diff --git a/api/logics.py b/api/logics.py index 4dee2a8d1..260c4b581 100644 --- a/api/logics.py +++ b/api/logics.py @@ -1824,23 +1824,22 @@ def withdraw_rewards(cls, user, invoice, routing_budget_ppm): if user.robot.earned_rewards < 1: return False, new_error(3003) - num_satoshis = user.robot.earned_rewards + earned_rewards = user.robot.earned_rewards + num_satoshis = earned_rewards - if routing_budget_ppm is not None and routing_budget_ppm is not False: + if routing_budget_ppm not in [None, False, 0]: routing_budget_sats = float(num_satoshis) * ( float(routing_budget_ppm) / 1_000_000 ) num_satoshis = int(num_satoshis - routing_budget_sats) else: - # start deprecate in the future routing_budget_sats = int( max( num_satoshis * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")), float(config("MIN_FLAT_ROUTING_FEE_LIMIT_REWARD")), ) - ) # 1000 ppm or 2 sats - routing_budget_ppm = (routing_budget_sats / float(num_satoshis)) * 1_000_000 - # end deprecate + ) + routing_budget_ppm = 0 reward_payout = LNNode.validate_ln_invoice( invoice, num_satoshis, routing_budget_ppm @@ -1858,6 +1857,8 @@ def withdraw_rewards(cls, user, invoice, routing_budget_ppm): receiver=user, invoice=invoice, num_satoshis=num_satoshis, + routing_budget_ppm=routing_budget_ppm, + routing_budget_sats=routing_budget_sats, description=reward_payout["description"], payment_hash=reward_payout["payment_hash"], created_at=reward_payout["created_at"], @@ -1880,7 +1881,7 @@ def withdraw_rewards(cls, user, invoice, routing_budget_ppm): # If fails, adds the rewards again. else: - user.robot.earned_rewards = num_satoshis + user.robot.earned_rewards = earned_rewards user.robot.save(update_fields=["earned_rewards"]) return False, new_error(3005, {"failure_reason": failure_reason}) diff --git a/tests/test_trade_pipeline.py b/tests/test_trade_pipeline.py index 724ee49df..6e3283a46 100644 --- a/tests/test_trade_pipeline.py +++ b/tests/test_trade_pipeline.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import User from django.urls import reverse -from api.models import Currency, Order +from api.models import Currency, LNPayment, Order from api.tasks import cache_market from django.utils import timezone from django.contrib.admin.sites import AdminSite @@ -1852,13 +1852,20 @@ def test_withdraw_reward_after_unilateral_cancel_routing_budget(self): # Submit reward invoice path = reverse("reward") - invoice = add_invoice("robot", response.json()["earned_rewards"]) + earned_rewards = response.json()["earned_rewards"] + routing_budget_ppm = 1000 + routing_budget_sats = earned_rewards * routing_budget_ppm / 1_000_000 + invoice_amount = int(earned_rewards - routing_budget_sats) + invoice = add_invoice("robot", invoice_amount) signed_payout_invoice = sign_message( invoice, passphrase_path=f"tests/robots/{trade.taker_index}/token", private_key_path=f"tests/robots/{trade.taker_index}/enc_priv_key", ) - body = {"invoice": signed_payout_invoice, "routing_budget_ppm": 0} + body = { + "invoice": signed_payout_invoice, + "routing_budget_ppm": routing_budget_ppm, + } response = self.client.post(path, body, **taker_headers) @@ -1866,6 +1873,19 @@ def test_withdraw_reward_after_unilateral_cancel_routing_budget(self): self.assertResponse(response) self.assertTrue(response.json()["successful_withdrawal"]) + reward_payment = LNPayment.objects.get( + concept=LNPayment.Concepts.WITHREWA, + receiver__robot__hash_id=read_file( + f"tests/robots/{trade.taker_index}/hash_id" + ).strip(), + ) + self.assertEqual(reward_payment.routing_budget_ppm, routing_budget_ppm) + self.assertEqual( + reward_payment.routing_budget_sats, + Decimal(str(routing_budget_sats)), + ) + self.assertEqual(reward_payment.num_satoshis, invoice_amount) + def test_order_expires_after_fiat_sent(self): """ Tests the expiration of an order after fiat sent is confirmed