Fix invoice payouts. Add onchain summary to accounting days.

This commit is contained in:
Reckless_Satoshi 2022-06-16 15:45:36 -07:00
parent 5c87c5ad85
commit 43b85d79d4
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
9 changed files with 32 additions and 79 deletions

View File

@ -93,7 +93,7 @@ class LNNode:
response = cls.lightningstub.WalletBalance(request, response = cls.lightningstub.WalletBalance(request,
metadata=[("macaroon", metadata=[("macaroon",
MACAROON.hex())]) MACAROON.hex())])
print(response)
return {'total_balance': response.total_balance, return {'total_balance': response.total_balance,
'confirmed_balance': response.confirmed_balance, 'confirmed_balance': response.confirmed_balance,
'unconfirmed_balance': response.unconfirmed_balance} 'unconfirmed_balance': response.unconfirmed_balance}
@ -108,7 +108,7 @@ class LNNode:
metadata=[("macaroon", metadata=[("macaroon",
MACAROON.hex())]) MACAROON.hex())])
print(response)
return {'local_balance': response.local_balance.sat, return {'local_balance': response.local_balance.sat,
'remote_balance': response.remote_balance.sat, 'remote_balance': response.remote_balance.sat,
'unsettled_local_balance': response.unsettled_local_balance.sat, 'unsettled_local_balance': response.unsettled_local_balance.sat,
@ -208,22 +208,17 @@ class LNNode:
metadata=[("macaroon", metadata=[("macaroon",
MACAROON.hex()) MACAROON.hex())
]) ])
print("status here")
print(response.state)
# TODO ERROR HANDLING
# Will fail if 'unable to locate invoice'. Happens if invoice expiry # 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 # time has passed (but these are 15% padded at the moment). Should catch it
# and report back that the invoice has expired (better robustness) # and report back that the invoice has expired (better robustness)
if response.state == 0: # OPEN if response.state == 0: # OPEN
print("STATUS: OPEN")
pass pass
if response.state == 1: # SETTLED if response.state == 1: # SETTLED
pass pass
if response.state == 2: # CANCELLED if response.state == 2: # CANCELLED
pass pass
if response.state == 3: # ACCEPTED (LOCKED) if response.state == 3: # ACCEPTED (LOCKED)
print("STATUS: ACCEPTED")
lnpayment.expiry_height = response.htlcs[0].expiry_height lnpayment.expiry_height = response.htlcs[0].expiry_height
lnpayment.status = LNPayment.Status.LOCKED lnpayment.status = LNPayment.Status.LOCKED
lnpayment.save() lnpayment.save()
@ -254,7 +249,6 @@ class LNNode:
try: try:
payreq_decoded = cls.decode_payreq(invoice) payreq_decoded = cls.decode_payreq(invoice)
print(payreq_decoded)
except: except:
payout["context"] = { payout["context"] = {
"bad_invoice": "Does not look like a valid lightning invoice" "bad_invoice": "Does not look like a valid lightning invoice"

View File

@ -725,7 +725,7 @@ class Logics:
concept=LNPayment.Concepts.PAYBUYER, concept=LNPayment.Concepts.PAYBUYER,
type=LNPayment.Types.NORM, type=LNPayment.Types.NORM,
sender=User.objects.get(username=ESCROW_USERNAME), 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. order, # In case this user has other payouts, update the one related to this order.
receiver=user, receiver=user,
# if there is a LNPayment matching these above, it updates that one with defaults below. # if there is a LNPayment matching these above, it updates that one with defaults below.

View File

@ -87,7 +87,7 @@ def follow_send_payment(hash):
timeout_seconds=timeout_seconds, timeout_seconds=timeout_seconds,
) )
order = lnpayment.order_paid order = lnpayment.order_paid_LN
try: try:
for response in LNNode.routerstub.SendPaymentV2(request, for response in LNNode.routerstub.SendPaymentV2(request,
metadata=[ metadata=[

View File

@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from control.models import AccountingDay, AccountingMonth, BalanceLog from control.models import AccountingDay, BalanceLog
from import_export.admin import ImportExportModelAdmin from import_export.admin import ImportExportModelAdmin
# Register your models here. # Register your models here.
@ -17,6 +17,7 @@ class AccountingDayAdmin(ImportExportModelAdmin):
"inflow", "inflow",
"outflow", "outflow",
"routing_fees", "routing_fees",
"mining_fees",
"cashflow", "cashflow",
"outstanding_earned_rewards", "outstanding_earned_rewards",
"outstanding_pending_disputes", "outstanding_pending_disputes",
@ -28,31 +29,6 @@ class AccountingDayAdmin(ImportExportModelAdmin):
change_links = ["day"] change_links = ["day"]
search_fields = ["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) @admin.register(BalanceLog)
class BalanceLogAdmin(ImportExportModelAdmin): class BalanceLogAdmin(ImportExportModelAdmin):

View File

@ -23,6 +23,8 @@ class AccountingDay(models.Model):
outflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) outflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Total cost in routing fees # Total cost in routing fees
routing_fees = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) 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 # Total inflows minus outflows and routing fees
cashflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) 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) # 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 on day
rewards_claimed = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False) 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): class BalanceLog(models.Model):
def get_total(): def get_total():

View File

@ -6,7 +6,7 @@ def do_accounting():
Does all accounting from the beginning of time 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 control.models import AccountingDay
from django.utils import timezone from django.utils import timezone
from datetime import timedelta from datetime import timedelta
@ -36,14 +36,16 @@ def do_accounting():
result = {} result = {}
while day <= today: while day <= today:
day_payments = all_payments.filter(created_at__gte=day,created_at__lte=day+timedelta(days=1)) 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)) 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'] contracted = day_ticks.aggregate(Sum('volume'))['volume__sum']
num_contracts = day_ticks.count() num_contracts = day_ticks.count()
inflow = day_payments.filter(type=LNPayment.Types.HOLD,status=LNPayment.Status.SETLED).aggregate(Sum('num_satoshis'))['num_satoshis__sum'] 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'] 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'] 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 contracted = 0 if contracted == None else contracted
@ -59,6 +61,7 @@ def do_accounting():
inflow = inflow, inflow = inflow,
outflow = outflow, outflow = outflow,
routing_fees = routing_fees, routing_fees = routing_fees,
mining_fees = mining_fees,
cashflow = inflow - outflow - routing_fees, cashflow = inflow - outflow - routing_fees,
rewards_claimed = rewards_claimed, rewards_claimed = rewards_claimed,
) )
@ -70,10 +73,21 @@ def do_accounting():
payouts_paid = 0 payouts_paid = 0
routing_cost = 0 routing_cost = 0
for payout in payouts: 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 payouts_paid += payout.num_satoshis
routing_cost += payout.fee 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 # account for those orders where bonds were lost
# + Settled bonds / bond_split # + Settled bonds / bond_split
bonds_settled = day_payments.filter(type=LNPayment.Types.HOLD,concept__in=[LNPayment.Concepts.TAKEBOND,LNPayment.Concepts.MAKEBOND], status=LNPayment.Status.SETLED) bonds_settled = day_payments.filter(type=LNPayment.Types.HOLD,concept__in=[LNPayment.Concepts.TAKEBOND,LNPayment.Concepts.MAKEBOND], status=LNPayment.Status.SETLED)
@ -117,8 +131,8 @@ def compute_node_balance():
''' '''
Queries LND for channel and wallet balance Queries LND for channel and wallet balance
''' '''
from control.models import BalanceLog
from control.models import BalanceLog
BalanceLog.objects.create() BalanceLog.objects.create()
return return

View File

@ -936,7 +936,7 @@ class TradeBox extends Component {
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">
<Typography variant="subtitle1"> <Typography variant="subtitle1">
<b>{t("Your invoice looks good!")}</b> {" " + this.stepXofY()} <b>{t("Your info looks good!")}</b> {" " + this.stepXofY()}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} align="center"> <Grid item xs={12} align="center">

View File

@ -181,6 +181,7 @@
"You do not have previous orders":"You do not have previous orders", "You do not have previous orders":"You do not have previous orders",
"Join RoboSats' Subreddit":"Join RoboSats' Subreddit", "Join RoboSats' Subreddit":"Join RoboSats' Subreddit",
"RoboSats in Reddit":"RoboSats in Reddit", "RoboSats in Reddit":"RoboSats in Reddit",
"Current onchain payout fee":"Current onchain payout fee",
"ORDER PAGE - OrderPage.js": "Order details page", "ORDER PAGE - OrderPage.js": "Order details page",
"Order Box":"Order Box", "Order Box":"Order Box",
@ -316,7 +317,7 @@
"Submit an invoice for {{amountSats}} Sats":"Submit an invoice for {{amountSats}} Sats", "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.", "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", "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.", "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).", "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!", "The trade collateral is locked!":"The trade collateral is locked!",

View File

@ -181,6 +181,7 @@
"You do not have previous orders":"No tienes órdenes previas", "You do not have previous orders":"No tienes órdenes previas",
"Join RoboSats' Subreddit":"Únete al subreddit de RoboSats", "Join RoboSats' Subreddit":"Únete al subreddit de RoboSats",
"RoboSats in Reddit":"RoboSats en Reddit", "RoboSats in Reddit":"RoboSats en Reddit",
"Current onchain payout fee":"Coste por envio onchain actual",
"ORDER PAGE - OrderPage.js": "Order details page", "ORDER PAGE - OrderPage.js": "Order details page",
"Order Box": "Orden", "Order Box": "Orden",