Improve logics around locked bonds. Add frontend confirm cancel dialog.

This commit is contained in:
Reckless_Satoshi 2022-01-18 07:23:57 -08:00
parent e31bc1adad
commit c58070f437
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
6 changed files with 95 additions and 63 deletions

View File

@ -125,7 +125,7 @@ class Logics():
elif order.status == Order.Status.WFB:
order.status = Order.Status.EXP
cls.cancel_bond(order.maker_bond)
order.maker = None
order.maker = None # TODO with the new validate_already_maker_taker there is no need to kick out participants on expired orders.
order.taker = None
order.save()
return True
@ -175,12 +175,10 @@ class Logics():
else:
cls.settle_bond(order.taker_bond)
cls.cancel_escrow(order)
order.status = Order.Status.PUB
order.taker = None
order.taker_bond = None
order.trade_escrow = None
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])
order.save()
cls.publish_order(order)
return True
elif order.status == Order.Status.WFI:
@ -203,12 +201,10 @@ class Logics():
else:
cls.settle_bond(order.taker_bond)
cls.return_escrow(order)
order.status = Order.Status.PUB
order.taker = None
order.taker_bond = None
order.trade_escrow = None
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])
order.save()
cls.publish_order(order)
return True
elif order.status == Order.Status.CHA:
@ -218,7 +214,8 @@ class Logics():
cls.open_dispute(order)
return True
def kick_taker(order):
@classmethod
def kick_taker(cls, order):
''' The taker did not lock the taker_bond. Now he has to go'''
# Add a time out to the taker
profile = order.taker.profile
@ -226,11 +223,9 @@ class Logics():
profile.save()
# Make order public again
order.status = Order.Status.PUB
order.taker = None
order.taker_bond = None
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])
order.save()
cls.publish_order(order)
return True
@classmethod
@ -417,14 +412,12 @@ class Logics():
# 4.b) When taker cancel after bond (before escrow)
'''The order into cancelled status if maker cancels.'''
elif order.status > Order.Status.TAK and order.status < Order.Status.CHA and order.taker == user:
elif order.status in [Order.Status.WF2, Order.Status.WFE] and order.taker == user:
# Settle the maker bond (Maker loses the bond for canceling an ongoing trade)
valid = cls.settle_bond(order.taker_bond)
if valid:
order.taker = None
order.status = Order.Status.PUB
# order.taker_bond = None # TODO fix this, it overrides the information about the settled taker bond. Might make admin tasks hard.
order.save()
cls.publish_order(order)
return True, None
# 5) When trade collateral has been posted (after escrow)
@ -437,12 +430,10 @@ class Logics():
return False, {'bad_request':'You cannot cancel this order'}
def publish_order(order):
if order.status == Order.Status.WFB:
order.status = Order.Status.PUB
# With the bond confirmation the order is extended 'public_order_duration' hours
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])
order.save()
return
order.status = Order.Status.PUB
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])
order.save()
return
@classmethod
def is_maker_bond_locked(cls, order):

View File

@ -166,14 +166,14 @@ class OrderView(viewsets.ViewSet):
data['escrow_locked'] = False
# If both bonds are locked, participants can see the final trade amount in sats.
if order.taker_bond:
if order.maker_bond.status == order.taker_bond.status == LNPayment.Status.LOCKED:
# Seller sees the amount he sends
if data['is_seller']:
data['trade_satoshis'] = order.last_satoshis
# Buyer sees the amount he receives
elif data['is_buyer']:
data['trade_satoshis'] = Logics.buyer_invoice_amount(order, request.user)[1]['invoice_amount']
# if order.taker_bond:
# if order.maker_bond.status == order.taker_bond.status == LNPayment.Status.LOCKED:
# # Seller sees the amount he sends
# if data['is_seller']:
# data['trade_satoshis'] = order.last_satoshis
# # Buyer sees the amount he receives
# elif data['is_buyer']:
# data['trade_satoshis'] = Logics.buyer_invoice_amount(order, request.user)[1]['invoice_amount']
# 5) If status is 'waiting for maker bond' and user is MAKER, reply with a MAKER hold invoice.
if order.status == Order.Status.WFB and data['is_maker']:

View File

@ -166,10 +166,10 @@ export default class BookPage extends Component {
style: {textAlign:"center"}
}}
onChange={this.handleCurrencyChange}
> <MenuItem value={0}>ANY</MenuItem>
> <MenuItem value={0}>🌍 ANY</MenuItem>
{
Object.entries(this.state.currencies_dict)
.map( ([key, value]) => <MenuItem value={parseInt(key)}>{value}</MenuItem> )
.map( ([key, value]) => <MenuItem value={parseInt(key)}>{getFlags(value) + " " + value}</MenuItem> )
}
</Select>
</FormControl>

View File

@ -1,5 +1,5 @@
import React, { Component } from "react";
import { Alert, Paper, CircularProgress, Button , Grid, Typography, List, ListItem, ListItemIcon, ListItemText, ListItemAvatar, Avatar, Divider, Box, LinearProgress} from "@mui/material"
import { Alert, Paper, CircularProgress, Button , Grid, Typography, List, ListItem, ListItemIcon, ListItemText, ListItemAvatar, Avatar, Divider, Box, LinearProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@mui/material"
import Countdown, { zeroPad, calcTimeDelta } from 'react-countdown';
import TradeBox from "./TradeBox";
import getFlags from './getFlags'
@ -11,6 +11,7 @@ import PriceChangeIcon from '@mui/icons-material/PriceChange';
import PaymentsIcon from '@mui/icons-material/Payments';
import MoneyIcon from '@mui/icons-material/Money';
import ArticleIcon from '@mui/icons-material/Article';
import ContentCopy from "@mui/icons-material/ContentCopy";
function getCookie(name) {
let cookieValue = null;
@ -43,6 +44,7 @@ export default class OrderPage extends Component {
currencies_dict: {"1":"USD"},
total_secs_expiry: 300,
loading: true,
openCancel: false,
};
this.orderId = this.props.match.params.orderId;
this.getCurrencyDict();
@ -144,8 +146,8 @@ export default class OrderPage extends Component {
countdownRenderer = ({ total, hours, minutes, seconds, completed }) => {
if (completed) {
// Render a completed state
this.getOrderDetails();
return null;
return (<span> The order has expired</span>);
} else {
var col = 'black'
var fraction_left = (total/1000) / this.state.total_secs_expiry
@ -218,7 +220,7 @@ export default class OrderPage extends Component {
return code
}
handleClickCancelOrderButton=()=>{
handleClickConfirmCancelButton=()=>{
console.log(this.state)
const requestOptions = {
method: 'POST',
@ -230,6 +232,64 @@ export default class OrderPage extends Component {
fetch('/api/order/' + '?order_id=' + this.orderId, requestOptions)
.then((response) => response.json())
.then((data) => (console.log(data) & this.getOrderDetails(data.id)));
this.handleClickCloseConfirmCancelDialog();
}
handleClickOpenConfirmCancelDialog = () => {
this.setState({openCancel: true});
};
handleClickCloseConfirmCancelDialog = () => {
this.setState({openCancel: false});
};
CancelDialog =() =>{
return(
<Dialog
open={this.state.openCancel}
onClose={this.handleClickCloseConfirmCancelDialog}
aria-labelledby="cancel-dialog-title"
aria-describedby="cancel-dialog-description"
>
<DialogTitle id="cancel-dialog-title">
{"Cancel the order?"}
</DialogTitle>
<DialogContent>
<DialogContentText id="cancel-dialog-description">
If the order is cancelled now you will lose your bond.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickCloseConfirmCancelDialog} autoFocus>Go back</Button>
<Button onClick={this.handleClickConfirmCancelButton}> Confirm Cancel </Button>
</DialogActions>
</Dialog>
)
}
CancelButton = () => {
// If maker and Waiting for Bond. Or if taker and Waiting for bond.
// Simply allow to cancel without showing the cancel dialog.
if ((this.state.isMaker & this.state.statusCode == 0) || this.state.isTaker & this.state.statusCode == 3){
return(
<Grid item xs={12} align="center">
<Button variant='contained' color='secondary' onClick={this.handleClickConfirmCancelButton}>Cancel</Button>
</Grid>
)}
// If the order does not yet have an escrow deposited. Show dialog
// to confirm forfeiting the bond
if (this.state.statusCode < 8){
return(
<Grid item xs={12} align="center">
<this.CancelDialog/>
<Button variant='contained' color='secondary' onClick={this.handleClickOpenConfirmCancelDialog}>Cancel</Button>
</Grid>
)}
// TODO If the escrow is Locked, show the collaborative cancel button.
// If none of the above do not return a cancel button.
return(null)
}
orderBox=()=>{
@ -346,8 +406,10 @@ export default class OrderPage extends Component {
</Paper>
</Grid>
{/* Participants cannot see the Back or Take Order buttons */}
{this.state.isParticipant ? "" :
{/* Participants can see the "Cancel" Button, but cannot see the "Back" or "Take Order" buttons */}
{this.state.isParticipant ?
<this.CancelButton/>
:
<>
<Grid item xs={12} align="center">
<Button variant='contained' color='primary' onClick={this.handleClickTakeOrderButton}>Take Order</Button>
@ -358,27 +420,7 @@ export default class OrderPage extends Component {
</>
}
{/* Makers can cancel before trade escrow deposited (status <9)*/}
{/* Only free cancel before bond locked (status 0)*/}
{this.state.isMaker & this.state.statusCode < 9 ?
<Grid item xs={12} align="center">
<Button variant='contained' color='secondary' onClick={this.handleClickCancelOrderButton}>Cancel</Button>
</Grid>
:""}
{this.state.isMaker & this.state.statusCode > 0 & this.state.statusCode < 9 ?
<Grid item xs={12} align="center">
<Typography color="secondary" variant="subtitle2" component="subtitle2">Cancelling now forfeits the maker bond</Typography>
</Grid>
:""}
{/* Takers can cancel before commiting the bond (status 3)*/}
{this.state.isTaker & this.state.statusCode == 3 ?
<Grid item xs={12} align="center">
<Button variant='contained' color='secondary' onClick={this.handleClickCancelOrderButton}>Cancel</Button>
</Grid>
:""}
</Grid>
</Grid>
)
}

View File

@ -183,7 +183,7 @@ export default class TradeBox extends Component {
return (
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography color="primary" component="subtitle1" variant="subtitle1">
<Typography color="green" component="subtitle1" variant="subtitle1">
<b>Deposit {pn(this.props.data.escrowSatoshis)} Sats as trade collateral </b>
</Typography>
</Grid>
@ -569,7 +569,6 @@ handleRatingChange=(e)=>{
)
}
render() {
return (
<Grid container spacing={1} style={{ width:330}}>

View File

@ -150,15 +150,15 @@ export default class UserGenPage extends Component {
<Grid container align="center">
<Grid item xs={12} align="center">
<IconButton onClick= {()=>navigator.clipboard.writeText(this.state.token)}>
<ContentCopy color='secondary'/>
<ContentCopy/>
</IconButton>
<TextField
//sx={{ input: { color: 'purple' } }}
InputLabelProps={{
style: { color: 'purple' },
style: { color: 'green' },
}}
error={this.state.bad_request}
label='Token - Store safely'
label='Store your token safely'
required='true'
value={this.state.token}
variant='standard'