From 43b85d79d4bd119c437227c2de29732b48ff01ec Mon Sep 17 00:00:00 2001 From: Reckless_Satoshi Date: Thu, 16 Jun 2022 15:45:36 -0700 Subject: [PATCH] Fix invoice payouts. Add onchain summary to accounting days. --- api/lightning/node.py | 10 ++------ api/logics.py | 2 +- api/tasks.py | 2 +- control/admin.py | 28 ++-------------------- control/models.py | 37 ++--------------------------- control/tasks.py | 26 +++++++++++++++----- frontend/src/components/TradeBox.js | 2 +- frontend/src/locales/en.json | 3 ++- frontend/src/locales/es.json | 1 + 9 files changed, 32 insertions(+), 79 deletions(-) diff --git a/api/lightning/node.py b/api/lightning/node.py index 655d5528..015c2dc0 100644 --- a/api/lightning/node.py +++ b/api/lightning/node.py @@ -93,7 +93,7 @@ class LNNode: response = cls.lightningstub.WalletBalance(request, metadata=[("macaroon", MACAROON.hex())]) - print(response) + return {'total_balance': response.total_balance, 'confirmed_balance': response.confirmed_balance, 'unconfirmed_balance': response.unconfirmed_balance} @@ -108,7 +108,7 @@ class LNNode: metadata=[("macaroon", MACAROON.hex())]) - print(response) + return {'local_balance': response.local_balance.sat, 'remote_balance': response.remote_balance.sat, 'unsettled_local_balance': response.unsettled_local_balance.sat, @@ -208,22 +208,17 @@ class LNNode: metadata=[("macaroon", MACAROON.hex()) ]) - print("status here") - print(response.state) - # TODO ERROR HANDLING # Will fail if 'unable to locate invoice'. Happens if invoice expiry # time has passed (but these are 15% padded at the moment). Should catch it # and report back that the invoice has expired (better robustness) if response.state == 0: # OPEN - print("STATUS: OPEN") pass if response.state == 1: # SETTLED pass if response.state == 2: # CANCELLED pass if response.state == 3: # ACCEPTED (LOCKED) - print("STATUS: ACCEPTED") lnpayment.expiry_height = response.htlcs[0].expiry_height lnpayment.status = LNPayment.Status.LOCKED lnpayment.save() @@ -254,7 +249,6 @@ class LNNode: try: payreq_decoded = cls.decode_payreq(invoice) - print(payreq_decoded) except: payout["context"] = { "bad_invoice": "Does not look like a valid lightning invoice" diff --git a/api/logics.py b/api/logics.py index f7b9a8a9..11fb8384 100644 --- a/api/logics.py +++ b/api/logics.py @@ -725,7 +725,7 @@ class Logics: concept=LNPayment.Concepts.PAYBUYER, type=LNPayment.Types.NORM, sender=User.objects.get(username=ESCROW_USERNAME), - order_paid= + order_paid_LN= order, # In case this user has other payouts, update the one related to this order. receiver=user, # if there is a LNPayment matching these above, it updates that one with defaults below. diff --git a/api/tasks.py b/api/tasks.py index 469fd1c0..c9e0052d 100644 --- a/api/tasks.py +++ b/api/tasks.py @@ -87,7 +87,7 @@ def follow_send_payment(hash): timeout_seconds=timeout_seconds, ) - order = lnpayment.order_paid + order = lnpayment.order_paid_LN try: for response in LNNode.routerstub.SendPaymentV2(request, metadata=[ diff --git a/control/admin.py b/control/admin.py index 1287b114..2740c408 100755 --- a/control/admin.py +++ b/control/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from control.models import AccountingDay, AccountingMonth, BalanceLog +from control.models import AccountingDay, BalanceLog from import_export.admin import ImportExportModelAdmin # Register your models here. @@ -17,6 +17,7 @@ class AccountingDayAdmin(ImportExportModelAdmin): "inflow", "outflow", "routing_fees", + "mining_fees", "cashflow", "outstanding_earned_rewards", "outstanding_pending_disputes", @@ -28,31 +29,6 @@ class AccountingDayAdmin(ImportExportModelAdmin): change_links = ["day"] search_fields = ["day"] -@admin.register(AccountingMonth) -class AccountingMonthAdmin(ImportExportModelAdmin): - - list_display = ( - "month", - "contracted", - "num_contracts", - "net_settled", - "net_paid", - "net_balance", - "inflow", - "outflow", - "routing_fees", - "cashflow", - "outstanding_earned_rewards", - "outstanding_pending_disputes", - "lifetime_rewards_claimed", - "outstanding_earned_rewards", - "pending_disputes", - "rewards_claimed", - ) - change_links = ["month"] - search_fields = ["month"] - - @admin.register(BalanceLog) class BalanceLogAdmin(ImportExportModelAdmin): diff --git a/control/models.py b/control/models.py index 7222e04a..cc3af699 100755 --- a/control/models.py +++ b/control/models.py @@ -23,6 +23,8 @@ class AccountingDay(models.Model): outflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) # Total cost in routing fees routing_fees = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) + # Total cost in minig fees + mining_fees = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) # Total inflows minus outflows and routing fees cashflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) # Balance on earned rewards (referral rewards, slashed bonds and solved disputes) @@ -38,41 +40,6 @@ class AccountingDay(models.Model): # Rewards claimed on day rewards_claimed = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) -class AccountingMonth(models.Model): - month = models.DateTimeField(primary_key=True, auto_now=False, auto_now_add=False) - - # Every field is denominated in Sats with (3 decimals for millisats) - # Total volume contracted - contracted = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Number of contracts - num_contracts = models.BigIntegerField(default=0, null=False, blank=False) - # Net volume of trading invoices settled (excludes disputes) - net_settled = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Net volume of trading invoices paid (excludes rewards and disputes) - net_paid = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Sum of net settled and net paid - net_balance = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Total volume of invoices settled - inflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Total volume of invoices paid - outflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Total cost in routing fees - routing_fees = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Total inflows minus outflows and routing fees - cashflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Balance on earned rewards (referral rewards, slashed bonds and solved disputes) - outstanding_earned_rewards = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Balance on pending disputes (not resolved yet) - outstanding_pending_disputes = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Rewards claimed lifetime - lifetime_rewards_claimed = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Balance change from last day on earned rewards (referral rewards, slashed bonds and solved disputes) - earned_rewards = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Balance change on pending disputes (not resolved yet) - pending_disputes = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - # Rewards claimed on day - rewards_claimed = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) - class BalanceLog(models.Model): def get_total(): diff --git a/control/tasks.py b/control/tasks.py index 333362cb..69905b6f 100644 --- a/control/tasks.py +++ b/control/tasks.py @@ -6,7 +6,7 @@ def do_accounting(): Does all accounting from the beginning of time ''' - from api.models import Order, LNPayment, Profile, MarketTick + from api.models import Order, LNPayment, OnchainPayment, Profile, MarketTick from control.models import AccountingDay from django.utils import timezone from datetime import timedelta @@ -36,14 +36,16 @@ def do_accounting(): result = {} while day <= today: day_payments = all_payments.filter(created_at__gte=day,created_at__lte=day+timedelta(days=1)) + day_onchain_payments = OnchainPayment.objects.filter(created_at__gte=day,created_at__lte=day+timedelta(days=1)) day_ticks = all_ticks.filter(timestamp__gte=day,timestamp__lte=day+timedelta(days=1)) - # Coarse accounting based on LNpayment objects + # Coarse accounting based on LNpayment and OnchainPayment objects contracted = day_ticks.aggregate(Sum('volume'))['volume__sum'] num_contracts = day_ticks.count() inflow = day_payments.filter(type=LNPayment.Types.HOLD,status=LNPayment.Status.SETLED).aggregate(Sum('num_satoshis'))['num_satoshis__sum'] - outflow = day_payments.filter(type=LNPayment.Types.NORM,status=LNPayment.Status.SUCCED).aggregate(Sum('num_satoshis'))['num_satoshis__sum'] + outflow = day_payments.filter(type=LNPayment.Types.NORM,status=LNPayment.Status.SUCCED).aggregate(Sum('num_satoshis'))['num_satoshis__sum'] + day_onchain_payments.filter(status__in=[OnchainPayment.Status.MEMPO,OnchainPayment.Status.CONFI]).aggregate(Sum('sent_satoshis'))['sent_satoshis__sum'] routing_fees = day_payments.filter(type=LNPayment.Types.NORM,status=LNPayment.Status.SUCCED).aggregate(Sum('fee'))['fee__sum'] + mining_fees = day_onchain_payments.filter(status__in=[OnchainPayment.Status.MEMPO,OnchainPayment.Status.CONFI]).aggregate(Sum('mining_fee_sats'))['mining_fee_sats__sum'] rewards_claimed = day_payments.filter(type=LNPayment.Types.NORM,concept=LNPayment.Concepts.WITHREWA,status=LNPayment.Status.SUCCED).aggregate(Sum('num_satoshis'))['num_satoshis__sum'] contracted = 0 if contracted == None else contracted @@ -59,6 +61,7 @@ def do_accounting(): inflow = inflow, outflow = outflow, routing_fees = routing_fees, + mining_fees = mining_fees, cashflow = inflow - outflow - routing_fees, rewards_claimed = rewards_claimed, ) @@ -70,9 +73,20 @@ def do_accounting(): payouts_paid = 0 routing_cost = 0 for payout in payouts: - escrows_settled += payout.order_paid.trade_escrow.num_satoshis + escrows_settled += payout.order_paid_LN.trade_escrow.num_satoshis payouts_paid += payout.num_satoshis routing_cost += payout.fee + + # Same for orders that use onchain payments. + payouts_tx = day_onchain_payments.filter(status__in=[OnchainPayment.Status.MEMPO,OnchainPayment.Status.CONFI]) + escrows_settled = 0 + payouts_tx_paid = 0 + mining_cost = 0 + for payout_tx in payouts_tx: + escrows_settled += payout_tx.order_paid_TX.trade_escrow.num_satoshis + payouts_tx_paid += payout_tx.sent_satoshis + mining_cost += payout_tx.fee + # account for those orders where bonds were lost # + Settled bonds / bond_split @@ -117,8 +131,8 @@ def compute_node_balance(): ''' Queries LND for channel and wallet balance ''' + from control.models import BalanceLog - BalanceLog.objects.create() - + return \ No newline at end of file diff --git a/frontend/src/components/TradeBox.js b/frontend/src/components/TradeBox.js index 2f31c3ac..527e6ec8 100644 --- a/frontend/src/components/TradeBox.js +++ b/frontend/src/components/TradeBox.js @@ -936,7 +936,7 @@ class TradeBox extends Component { - {t("Your invoice looks good!")} {" " + this.stepXofY()} + {t("Your info looks good!")} {" " + this.stepXofY()} diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 73f1ba85..659907e9 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -181,6 +181,7 @@ "You do not have previous orders":"You do not have previous orders", "Join RoboSats' Subreddit":"Join RoboSats' Subreddit", "RoboSats in Reddit":"RoboSats in Reddit", + "Current onchain payout fee":"Current onchain payout fee", "ORDER PAGE - OrderPage.js": "Order details page", "Order Box":"Order Box", @@ -316,7 +317,7 @@ "Submit an invoice for {{amountSats}} Sats":"Submit an invoice for {{amountSats}} Sats", "The taker is committed! Before letting you send {{amountFiat}} {{currencyCode}}, we want to make sure you are able to receive the BTC. Please provide a valid invoice for {{amountSats}} Satoshis.":"The taker is committed! Before letting you send {{amountFiat}} {{currencyCode}}, we want to make sure you are able to receive the BTC. Please provide a valid invoice for {{amountSats}} Satoshis.", "Payout Lightning Invoice":"Payout Lightning Invoice", - "Your invoice looks good!":"Your invoice looks good!", + "Your info looks good!":"Your info looks good!", "We are waiting for the seller to lock the trade amount.":"We are waiting for the seller to lock the trade amount.", "Just hang on for a moment. If the seller does not deposit, you will get your bond back automatically. In addition, you will receive a compensation (check the rewards in your profile).":"Just hang on for a moment. If the seller does not deposit, you will get your bond back automatically. In addition, you will receive a compensation (check the rewards in your profile).", "The trade collateral is locked!":"The trade collateral is locked!", diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index e68d222f..2fae7ad9 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -181,6 +181,7 @@ "You do not have previous orders":"No tienes órdenes previas", "Join RoboSats' Subreddit":"Únete al subreddit de RoboSats", "RoboSats in Reddit":"RoboSats en Reddit", + "Current onchain payout fee":"Coste por envio onchain actual", "ORDER PAGE - OrderPage.js": "Order details page", "Order Box": "Orden",