mirror of
https://github.com/RoboSats/robosats.git
synced 2025-08-05 09:00:19 +00:00
add option for routing_budget_ppm in /api/rewards
This commit is contained in:
@ -139,7 +139,6 @@ EXP_TAKER_BOND_INVOICE = 200
|
|||||||
# Proportional routing fee limit (fraction of total payout: % / 100)
|
# Proportional routing fee limit (fraction of total payout: % / 100)
|
||||||
PROPORTIONAL_ROUTING_FEE_LIMIT = 0.001
|
PROPORTIONAL_ROUTING_FEE_LIMIT = 0.001
|
||||||
# Base flat limit fee for routing in Sats (used only when proportional is lower than this)
|
# Base flat limit fee for routing in Sats (used only when proportional is lower than this)
|
||||||
MIN_FLAT_ROUTING_FEE_LIMIT = 10
|
|
||||||
MIN_FLAT_ROUTING_FEE_LIMIT_REWARD = 2
|
MIN_FLAT_ROUTING_FEE_LIMIT_REWARD = 2
|
||||||
# Routing timeouts
|
# Routing timeouts
|
||||||
REWARDS_TIMEOUT_SECONDS = 30
|
REWARDS_TIMEOUT_SECONDS = 30
|
||||||
|
@ -501,7 +501,7 @@ class CLNNode:
|
|||||||
* float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")),
|
* 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
|
) # 1000 ppm or 2 sats
|
||||||
timeout_seconds = int(config("REWARDS_TIMEOUT_SECONDS"))
|
timeout_seconds = int(config("REWARDS_TIMEOUT_SECONDS"))
|
||||||
request = node_pb2.PayRequest(
|
request = node_pb2.PayRequest(
|
||||||
bolt11=lnpayment.invoice,
|
bolt11=lnpayment.invoice,
|
||||||
|
@ -472,7 +472,7 @@ class LNDNode:
|
|||||||
* float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")),
|
* 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
|
) # 1000 ppm or 2 sats
|
||||||
timeout_seconds = int(config("REWARDS_TIMEOUT_SECONDS"))
|
timeout_seconds = int(config("REWARDS_TIMEOUT_SECONDS"))
|
||||||
request = router_pb2.SendPaymentRequest(
|
request = router_pb2.SendPaymentRequest(
|
||||||
payment_request=lnpayment.invoice,
|
payment_request=lnpayment.invoice,
|
||||||
|
@ -1886,7 +1886,7 @@ class Logics:
|
|||||||
return
|
return
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def withdraw_rewards(cls, user, invoice):
|
def withdraw_rewards(cls, user, invoice, routing_budget_ppm):
|
||||||
# only a user with positive withdraw balance can use this
|
# only a user with positive withdraw balance can use this
|
||||||
|
|
||||||
if user.robot.earned_rewards < 1:
|
if user.robot.earned_rewards < 1:
|
||||||
@ -1894,14 +1894,22 @@ class Logics:
|
|||||||
|
|
||||||
num_satoshis = user.robot.earned_rewards
|
num_satoshis = user.robot.earned_rewards
|
||||||
|
|
||||||
|
if routing_budget_ppm is not None and routing_budget_ppm is not False:
|
||||||
|
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(
|
routing_budget_sats = int(
|
||||||
max(
|
max(
|
||||||
num_satoshis * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")),
|
num_satoshis * float(config("PROPORTIONAL_ROUTING_FEE_LIMIT")),
|
||||||
float(config("MIN_FLAT_ROUTING_FEE_LIMIT_REWARD")),
|
float(config("MIN_FLAT_ROUTING_FEE_LIMIT_REWARD")),
|
||||||
)
|
)
|
||||||
) # 1000 ppm or 10 sats
|
) # 1000 ppm or 2 sats
|
||||||
|
|
||||||
routing_budget_ppm = (routing_budget_sats / float(num_satoshis)) * 1_000_000
|
routing_budget_ppm = (routing_budget_sats / float(num_satoshis)) * 1_000_000
|
||||||
|
# end deprecate
|
||||||
|
|
||||||
reward_payout = LNNode.validate_ln_invoice(
|
reward_payout = LNNode.validate_ln_invoice(
|
||||||
invoice, num_satoshis, routing_budget_ppm
|
invoice, num_satoshis, routing_budget_ppm
|
||||||
)
|
)
|
||||||
|
@ -680,6 +680,14 @@ class ClaimRewardSerializer(serializers.Serializer):
|
|||||||
default=None,
|
default=None,
|
||||||
help_text="A valid LN invoice with the reward amount to withdraw",
|
help_text="A valid LN invoice with the reward amount to withdraw",
|
||||||
)
|
)
|
||||||
|
routing_budget_ppm = serializers.IntegerField(
|
||||||
|
default=0,
|
||||||
|
min_value=Decimal(0),
|
||||||
|
max_value=100_001,
|
||||||
|
allow_null=True,
|
||||||
|
required=False,
|
||||||
|
help_text="Max budget to allocate for routing in PPM",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PriceSerializer(serializers.Serializer):
|
class PriceSerializer(serializers.Serializer):
|
||||||
|
@ -904,6 +904,7 @@ class RewardView(CreateAPIView):
|
|||||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
pgp_invoice = serializer.data.get("invoice")
|
pgp_invoice = serializer.data.get("invoice")
|
||||||
|
routing_budget_ppm = serializer.data.get("routing_budget_ppm", None)
|
||||||
|
|
||||||
valid_signature, invoice = verify_signed_message(
|
valid_signature, invoice = verify_signed_message(
|
||||||
request.user.robot.public_key, pgp_invoice
|
request.user.robot.public_key, pgp_invoice
|
||||||
@ -915,7 +916,7 @@ class RewardView(CreateAPIView):
|
|||||||
status.HTTP_400_BAD_REQUEST,
|
status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
valid, context = Logics.withdraw_rewards(request.user, invoice)
|
valid, context = Logics.withdraw_rewards(request.user, invoice, routing_budget_ppm)
|
||||||
|
|
||||||
if not valid:
|
if not valid:
|
||||||
context["successful_withdrawal"] = False
|
context["successful_withdrawal"] = False
|
||||||
|
@ -1732,6 +1732,45 @@ class TradeTest(BaseAPITestCase):
|
|||||||
self.assertResponse(response)
|
self.assertResponse(response)
|
||||||
self.assertIsInstance(response.json()["earned_rewards"], int)
|
self.assertIsInstance(response.json()["earned_rewards"], int)
|
||||||
|
|
||||||
|
# Submit reward invoice
|
||||||
|
path = reverse("reward")
|
||||||
|
invoice = add_invoice("robot", response.json()["earned_rewards"])
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post(path, body, **taker_headers)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertResponse(response)
|
||||||
|
self.assertTrue(response.json()["successful_withdrawal"])
|
||||||
|
|
||||||
|
def test_withdraw_reward_after_unilateral_cancel_routing_budget(self):
|
||||||
|
"""
|
||||||
|
Tests withdraw rewards specifying routing_budget_ppm as taker after maker
|
||||||
|
cancels order unilaterally
|
||||||
|
"""
|
||||||
|
trade = Trade(self.client)
|
||||||
|
trade.publish_order()
|
||||||
|
trade.take_order()
|
||||||
|
trade.take_order_third()
|
||||||
|
trade.lock_taker_bond()
|
||||||
|
trade.cancel_order(trade.maker_index)
|
||||||
|
|
||||||
|
# Fetch amount of rewards for taker
|
||||||
|
path = reverse("robot")
|
||||||
|
taker_headers = trade.get_robot_auth(trade.taker_index)
|
||||||
|
response = self.client.get(path, **taker_headers)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertResponse(response)
|
||||||
|
self.assertIsInstance(response.json()["earned_rewards"], int)
|
||||||
|
|
||||||
# Submit reward invoice
|
# Submit reward invoice
|
||||||
path = reverse("reward")
|
path = reverse("reward")
|
||||||
invoice = add_invoice("robot", response.json()["earned_rewards"])
|
invoice = add_invoice("robot", response.json()["earned_rewards"])
|
||||||
@ -1742,6 +1781,7 @@ class TradeTest(BaseAPITestCase):
|
|||||||
)
|
)
|
||||||
body = {
|
body = {
|
||||||
"invoice": signed_payout_invoice,
|
"invoice": signed_payout_invoice,
|
||||||
|
"routing_budget_ppm": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
response = self.client.post(path, body, **taker_headers)
|
response = self.client.post(path, body, **taker_headers)
|
||||||
|
Reference in New Issue
Block a user