Add frontend input address components

This commit is contained in:
Reckless_Satoshi 2022-06-11 06:12:09 -07:00
parent b1d68a39f7
commit dc9d5e5e2a
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
6 changed files with 168 additions and 73 deletions

View File

@ -504,7 +504,7 @@ class Logics:
MIN_POINT = float(config('MIN_POINT'))
MAX_SWAP_FEE = float(config('MAX_SWAP_FEE'))
MAX_POINT = float(config('MAX_POINT'))
if balance.onchain_fraction > MIN_POINT:
if float(balance.onchain_fraction) > MIN_POINT:
swap_fee_rate = MIN_SWAP_FEE
else:
slope = (MAX_SWAP_FEE - MIN_SWAP_FEE) / (MAX_POINT - MIN_POINT)
@ -514,31 +514,44 @@ class Logics:
MIN_SWAP_FEE = float(config('MIN_SWAP_FEE'))
MAX_SWAP_FEE = float(config('MAX_SWAP_FEE'))
SWAP_LAMBDA = float(config('SWAP_LAMBDA'))
swap_fee_rate = MIN_SWAP_FEE + (MAX_SWAP_FEE - MIN_SWAP_FEE) * math.exp(-SWAP_LAMBDA * balance.onchain_fraction)
swap_fee_rate = MIN_SWAP_FEE + (MAX_SWAP_FEE - MIN_SWAP_FEE) * math.exp(-SWAP_LAMBDA * float(balance.onchain_fraction))
print("MIN_SWAP_FEE",MIN_SWAP_FEE)
print("MAX_SWAP_FEE",MAX_SWAP_FEE)
print("SWAP_LAMBDA",SWAP_LAMBDA)
print("swap_fee_rate",swap_fee_rate)
return swap_fee_rate
return swap_fee_rate * 100
@classmethod
def create_onchain_payment(cls, order, preliminary_amount):
def create_onchain_payment(cls, order, user, preliminary_amount):
'''
Creates an empty OnchainPayment for order.payout_tx.
It sets the fees to be applied to this order if onchain Swap is used.
If the user submits a LN invoice instead. The returned OnchainPayment goes unused.
'''
onchain_payment = OnchainPayment.objects.create()
onchain_payment = OnchainPayment.objects.create(receiver=user)
# Compute a safer available onchain liquidity: (confirmed_utxos - reserve - pending_outgoing_txs))
# Accounts for already committed outgoing TX for previous users.
confirmed = onchain_payment.balance.onchain_confirmed
reserve = 0.01 * onchain_payment.balance.total # We assume a reserve of 1%
pending_txs = OnchainPayment.objects.filter(status=OnchainPayment.Status.VALID).aggregate(Sum('num_satoshis'))['num_satoshis__sum']
if pending_txs == None:
pending_txs = 0
available_onchain = confirmed - reserve - pending_txs
if preliminary_amount > available_onchain: # Not enough onchain balance to commit for this swap.
return False
onchain_payment.suggested_mining_fee_rate = LNNode.estimate_fee(amount_sats=preliminary_amount)
onchain_payment.swap_fee_rate = cls.compute_swap_fee_rate(onchain_payment.preliminary_amount)
suggested_mining_fee_rate = LNNode.estimate_fee(amount_sats=preliminary_amount)["mining_fee_rate"]
# Hardcap mining fee suggested at 50 sats/vbyte
if suggested_mining_fee_rate > 50:
suggested_mining_fee_rate = 50
onchain_payment.suggested_mining_fee_rate = LNNode.estimate_fee(amount_sats=preliminary_amount)["mining_fee_rate"]
onchain_payment.swap_fee_rate = cls.compute_swap_fee_rate(onchain_payment.balance)
onchain_payment.save()
order.payout_tx = onchain_payment
@ -577,7 +590,7 @@ class Logics:
if order.payout_tx == None:
# Creates the OnchainPayment object and checks node balance
valid, _ = cls.create_onchain_payment(order, preliminary_amount=context["invoice_amount"])
valid = cls.create_onchain_payment(order, user, preliminary_amount=context["invoice_amount"])
if not valid:
context["swap_allowed"] = False
context["swap_failure_reason"] = "Not enough onchain liquidity available to offer swaps"

View File

@ -188,7 +188,7 @@ class OnchainPayment(models.Model):
default=Concepts.PAYBUYER)
status = models.PositiveSmallIntegerField(choices=Status.choices,
null=False,
default=Status.VALID)
default=Status.CREAT)
# payment info
address = models.CharField(max_length=100,
@ -203,10 +203,11 @@ class OnchainPayment(models.Model):
default=None,
blank=True)
num_satoshis = models.PositiveBigIntegerField(validators=[
MinValueValidator(0.7 * MIN_SWAP_AMOUNT),
MaxValueValidator(1.5 * MAX_TRADE),
])
num_satoshis = models.PositiveBigIntegerField(null=True,
validators=[
MinValueValidator(0.7 * MIN_SWAP_AMOUNT),
MaxValueValidator(1.5 * MAX_TRADE),
])
# fee in sats/vbyte with mSats decimals fee_msat
suggested_mining_fee_rate = models.DecimalField(max_digits=6,
@ -232,7 +233,7 @@ class OnchainPayment(models.Model):
swap_fee_rate = models.DecimalField(max_digits=4,
decimal_places=2,
default=2,
default=float(config("MIN_SWAP_FEE"))*100,
null=False,
blank=False)
@ -454,6 +455,8 @@ class Order(models.Model):
default=None,
blank=True,
)
# is buyer payout a LN invoice (false) or on chain address (true)
is_swap = models.BooleanField(default=False, null=False)
# buyer payment LN invoice
payout = models.OneToOneField(
LNPayment,
@ -463,15 +466,15 @@ class Order(models.Model):
default=None,
blank=True,
)
# payout_tx = models.OneToOneField(
# OnchainPayment,
# related_name="order_paid_TX",
# on_delete=models.SET_NULL,
# null=True,
# default=None,
# blank=True,
# )
# buyer payment address
payout_tx = models.OneToOneField(
OnchainPayment,
related_name="order_paid_TX",
on_delete=models.SET_NULL,
null=True,
default=None,
blank=True,
)
# ratings
maker_rated = models.BooleanField(default=False, null=False)

View File

@ -337,7 +337,7 @@ class OrderView(viewsets.ViewSet):
elif data["is_buyer"] and (order.status == Order.Status.WF2
or order.status == Order.Status.WFI):
# If the two bonds are locked, reply with an AMOUNT so he can send the buyer invoice.
# If the two bonds are locked, reply with an AMOUNT and onchain swap cost so he can send the buyer invoice/address
if (order.maker_bond.status == order.taker_bond.status ==
LNPayment.Status.LOCKED):
valid, context = Logics.payout_amount(order, request.user)

View File

@ -78,7 +78,7 @@ class BalanceLog(models.Model):
def get_total():
return LNNode.wallet_balance()['total_balance'] + LNNode.channel_balance()['local_balance']
def get_frac():
return (LNNode.wallet_balance()['total_balance'] + LNNode.channel_balance()['local_balance']) / LNNode.wallet_balance()['total_balance']
return LNNode.wallet_balance()['total_balance'] / (LNNode.wallet_balance()['total_balance'] + LNNode.channel_balance()['local_balance'])
def get_oc_total():
return LNNode.wallet_balance()['total_balance']
def get_oc_conf():

View File

@ -1,6 +1,6 @@
import React, { Component } from "react";
import { withTranslation, Trans} from "react-i18next";
import { IconButton, Box, Link, Paper, Rating, Button, Tooltip, CircularProgress, Grid, Typography, TextField, List, ListItem, ListItemText, Divider, ListItemIcon, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material"
import { Tabs, Tab, IconButton, Box, Link, Paper, Rating, Button, Tooltip, CircularProgress, Grid, Typography, TextField, List, ListItem, ListItemText, Divider, ListItemIcon, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material"
import QRCode from "react-qr-code";
import Countdown, { zeroPad} from 'react-countdown';
import Chat from "./EncryptedChat"
@ -18,6 +18,8 @@ import BalanceIcon from '@mui/icons-material/Balance';
import ContentCopy from "@mui/icons-material/ContentCopy";
import PauseCircleIcon from '@mui/icons-material/PauseCircle';
import PlayCircleIcon from '@mui/icons-material/PlayCircle';
import BoltIcon from '@mui/icons-material/Bolt';
import LinkIcon from '@mui/icons-material/Link';
import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
import { NewTabIcon } from "./Icons";
@ -33,6 +35,7 @@ class TradeBox extends Component {
openConfirmFiatReceived: false,
openConfirmDispute: false,
openEnableTelegram: false,
receiveTab: 0,
badInvoice: false,
badStatement: false,
qrscanner: false,
@ -599,57 +602,133 @@ class TradeBox extends Component {
{/* Make confirmation sound for HTLC received. */}
{this.Sound("locked-invoice")}
<Typography color="primary" variant="subtitle1">
<b> {t("Submit an invoice for {{amountSats}} Sats",{amountSats: pn(this.props.data.invoice_amount)})}
<b> {t("Submit payout info for {{amountSats}} Sats",{amountSats: pn(this.props.data.invoice_amount)})}
</b> {" " + this.stepXofY()}
</Typography>
</Grid>
<List dense={true}>
<Divider/>
<ListItem>
<Typography variant="body2">
{t("The taker is committed! Before letting you send {{amountFiat}} {{currencyCode}}, we want to make sure you are able to receive the BTC.",
{amountFiat: parseFloat(parseFloat(this.props.data.amount).toFixed(4)),
currencyCode: this.props.data.currencyCode})}
</Typography>
</ListItem>
<ListItem>
<Paper elevation={2}>
<Tabs value={this.state.receiveTab} variant="fullWidth">
<Tab disableRipple={true} label={<div style={{display:'flex', alignItems:'center', justifyContent:'center', flexWrap:'wrap'}}><BoltIcon/> Lightning</div>} onClick={() => this.setState({receiveTab:0})}/>
<Tab label={<div style={{display:'flex', alignItems:'center', justifyContent:'center', flexWrap:'wrap'}}><LinkIcon/> Onchain</div>} disabled={!this.props.data.swap_allowed} onClick={() => this.setState({receiveTab:1})} />
</Tabs>
</Paper>
{/* LIGHTNING PAYOUT TAB */}
<div style={{display: this.state.receiveTab == 0 ? '':'none'}}>
<div style={{height:15}}/>
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography variant="body2">
{t("Submit a valid invoice for {{amountSats}} Satoshis.",
{amountSats: pn(this.props.data.invoice_amount)})}
</Typography>
</Grid>
<Grid item xs={12} align="center">
{this.compatibleWalletsButton()}
</Grid>
<Grid item xs={12} align="center">
<TextField
error={this.state.badInvoice}
helperText={this.state.badInvoice ? t(this.state.badInvoice) : "" }
label={t("Payout Lightning Invoice")}
required
value={this.state.invoice}
inputProps={{
style: {textAlign:"center"},
maxHeight: 200,
}}
multiline
minRows={4}
maxRows={this.state.qrscanner ? 4 : 8}
onChange={this.handleInputInvoiceChanged}
/>
</Grid>
{this.state.qrscanner ?
<Grid item xs={12} align="center">
<QrReader
delay={300}
onError={this.handleError}
onScan={this.handleScan}
style={{ width: '75%' }}
/>
</Grid>
: null }
<Grid item xs={12} align="center">
<IconButton><QrCodeScannerIcon onClick={this.handleQRbutton}/></IconButton>
<Button onClick={this.handleClickSubmitInvoiceButton} variant='contained' color='primary'>{t("Submit")}</Button>
</Grid>
</Grid>
</div>
<Grid item xs={12} align="left">
<Typography variant="body2">
{t("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.",
{amountFiat: parseFloat(parseFloat(this.props.data.amount).toFixed(4)),
currencyCode: this.props.data.currencyCode,
amountSats: pn(this.props.data.invoice_amount)}
)
}
</Typography>
</Grid>
{/* ONCHAIN PAYOUT TAB */}
<div style={{display: this.state.receiveTab == 1 ? '':'none'}}>
<div style={{height:15}}/>
<Grid container spacing={1}>
<Grid item xs={12} align="left">
<Typography variant="body2">
{t("RoboSats will perform an on-the-fly reverse submarine swap and send to an onchain address for a fee. Submit onchain address. Preliminary {{amountSats}} Sats, swap allowed {{swapAllowed}}, swap fee {{swapFee}}%, mining fee suggested {{suggestedMiningFee}} Sats/vbyte",
{amountSats: this.props.data.invoice_amount,
swapAllowed: this.props.data.swap_allowed,
swapFee: this.props.data.swap_fee_rate ,
suggestedMiningFee: this.props.data.suggested_mining_fee_rate}
)
}
</Typography>
</Grid>
<Grid item xs={12} align="left">
<Typography variant="body2">
{t("Swap fee: {{swapFeeSats}}Sats ({{swapFeeRate}}%)",
{swapFeeSats: this.props.data.invoice_amount * this.state.swap_fee_rate/100,
swapFeeRate: this.state.swap_fee_rate})
}
</Typography>
<Typography variant="body2">
{t("Mining fee: {{miningFee}}Sats ({{swapFeeRate}}%)",
{miningFee: this.props.data.suggestedMiningFee * 141})}
</Typography>
<Typography variant="body2">
{t("You receive: {{onchainAmount}}Sats)",
{onchainAmount: pn(parseInt(this.props.data.invoice_amount - (this.props.data.suggestedMiningFee * 141) - (this.props.data.invoice_amount * this.state.swap_fee_rate/100)))})}
</Typography>
</Grid>
<Grid item xs={12} align="center">
<TextField
error={this.state.badAddress}
helperText={this.state.badAddress ? t(this.state.badAddress) : "" }
label={t("Payout Lightning Invoice")}
required
value={this.state.invoice}
inputProps={{
style: {textAlign:"center"},
maxHeight: 240,
}}
onChange={null}
/>
</Grid>
<Grid item xs={12} align="center">
{this.compatibleWalletsButton()}
</Grid>
<Grid item xs={12} align="center">
<Button onClick={null} variant='contained' color='primary'>{t("Submit")}</Button>
</Grid>
<Grid item xs={12} align="center">
<TextField
error={this.state.badInvoice}
helperText={this.state.badInvoice ? t(this.state.badInvoice) : "" }
label={t("Payout Lightning Invoice")}
required
value={this.state.invoice}
inputProps={{
style: {textAlign:"center"},
maxHeight: 200,
}}
multiline
minRows={5}
maxRows={this.state.qrscanner ? 5 : 10}
onChange={this.handleInputInvoiceChanged}
/>
</Grid>
{this.state.qrscanner ?
<Grid item xs={12} align="center">
<QrReader
delay={300}
onError={this.handleError}
onScan={this.handleScan}
style={{ width: '75%' }}
/>
</Grid>
: null }
<Grid item xs={12} align="center">
<IconButton><QrCodeScannerIcon onClick={this.handleQRbutton}/></IconButton>
<Button onClick={this.handleClickSubmitInvoiceButton} variant='contained' color='primary'>{t("Submit")}</Button>
</Grid>
</Grid>
</div>
</ListItem>
<Divider/>
</List>
{this.showBondIsLocked()}
</Grid>

View File

@ -128,7 +128,7 @@ class UserGenPage extends Component {
handleChangeToken=(e)=>{
this.setState({
token: e.target.value,
token: e.target.value.split(' ').join(''),
tokenHasChanged: true,
})
}