mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-31 02:21:35 +00:00
Implement collaborative cancellation
This commit is contained in:
parent
6377b052ce
commit
b934534e1e
@ -421,14 +421,46 @@ class Logics():
|
||||
return True, None
|
||||
|
||||
# 5) When trade collateral has been posted (after escrow)
|
||||
'''Always goes to cancelled status. Collaboration is needed.
|
||||
When a user asks for cancel, 'order.is_pending_cancel' goes True.
|
||||
'''Always goes to CCA status. Collaboration is needed.
|
||||
When a user asks for cancel, 'order.m/t/aker_asked_cancel' goes True.
|
||||
When the second user asks for cancel. Order is totally cancelled.
|
||||
Has a small cost for both parties to prevent node DDOS.'''
|
||||
Must have a small cost for both parties to prevent node DDOS.'''
|
||||
elif order.status in [Order.Status.WFI, Order.Status.CHA, Order.Status.FSE]:
|
||||
|
||||
# if the maker had asked, and now the taker does: cancel order, return everything
|
||||
if order.maker_asked_cancel and user == order.taker:
|
||||
cls.collaborative_cancel(order)
|
||||
return True, None
|
||||
|
||||
# if the taker had asked, and now the maker does: cancel order, return everything
|
||||
elif order.taker_asked_cancel and user == order.maker:
|
||||
cls.collaborative_cancel(order)
|
||||
return True, None
|
||||
|
||||
# Otherwise just make true the asked for cancel flags
|
||||
elif user == order.taker:
|
||||
order.taker_asked_cancel = True
|
||||
order.save()
|
||||
return True, None
|
||||
|
||||
elif user == order.maker:
|
||||
order.maker_asked_cancel = True
|
||||
order.save()
|
||||
return True, None
|
||||
|
||||
|
||||
else:
|
||||
return False, {'bad_request':'You cannot cancel this order'}
|
||||
|
||||
@classmethod
|
||||
def collaborative_cancel(cls, order):
|
||||
cls.return_bond(order.maker_bond)
|
||||
cls.return_bond(order.taker_bond)
|
||||
cls.return_escrow(order)
|
||||
order.status = Order.Status.CCA
|
||||
order.save()
|
||||
return
|
||||
|
||||
def publish_order(order):
|
||||
order.status = Order.Status.PUB
|
||||
order.expires_at = order.created_at + timedelta(seconds=Order.t_to_expire[Order.Status.PUB])
|
||||
|
@ -142,7 +142,8 @@ class Order(models.Model):
|
||||
# order participants
|
||||
maker = models.ForeignKey(User, related_name='maker', on_delete=models.SET_NULL, null=True, default=None) # unique = True, a maker can only make one order
|
||||
taker = models.ForeignKey(User, related_name='taker', on_delete=models.SET_NULL, null=True, default=None, blank=True) # unique = True, a taker can only take one order
|
||||
is_pending_cancel = models.BooleanField(default=False, null=False) # When collaborative cancel is needed and one partner has cancelled.
|
||||
maker_asked_cancel = models.BooleanField(default=False, null=False) # When collaborative cancel is needed and one partner has cancelled.
|
||||
taker_asked_cancel = models.BooleanField(default=False, null=False) # When collaborative cancel is needed and one partner has cancelled.
|
||||
is_fiat_sent = models.BooleanField(default=False, null=False)
|
||||
|
||||
# in dispute
|
||||
|
19
api/views.py
19
api/views.py
@ -189,7 +189,6 @@ class OrderView(viewsets.ViewSet):
|
||||
|
||||
# 7 a. ) If seller and status is 'WF2' or 'WFE'
|
||||
elif data['is_seller'] and (order.status == Order.Status.WF2 or order.status == Order.Status.WFE):
|
||||
|
||||
# If the two bonds are locked, reply with an ESCROW hold invoice.
|
||||
if order.maker_bond.status == order.taker_bond.status == LNPayment.Status.LOCKED:
|
||||
valid, context = Logics.gen_escrow_hold_invoice(order, request.user)
|
||||
@ -210,15 +209,23 @@ class OrderView(viewsets.ViewSet):
|
||||
return Response(context, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 8) If status is 'CHA' or 'FSE' and all HTLCS are in LOCKED
|
||||
elif order.status == Order.Status.CHA or order.status == Order.Status.FSE: # TODO Add the other status
|
||||
|
||||
elif order.status in [Order.Status.WFI, Order.Status.CHA, Order.Status.FSE]:
|
||||
print('CCCAAABBAAAAAAAAAAAAAAAA')
|
||||
# If all bonds are locked.
|
||||
if order.maker_bond.status == order.taker_bond.status == order.trade_escrow.status == LNPayment.Status.LOCKED:
|
||||
# add whether a collaborative cancel is pending
|
||||
data['pending_cancel'] = order.is_pending_cancel
|
||||
print('AAABBAAAAAAAAAAAAAAAA')
|
||||
# add whether a collaborative cancel is pending or has been asked
|
||||
if (data['is_maker'] and order.taker_asked_cancel) or (data['is_taker'] and order.maker_asked_cancel):
|
||||
print('PENDING')
|
||||
data['pending_cancel'] = True
|
||||
elif (data['is_maker'] and order.maker_asked_cancel) or (data['is_taker'] and order.taker_asked_cancel):
|
||||
print('ASKED')
|
||||
data['asked_for_cancel'] = True
|
||||
else:
|
||||
data['asked_for_cancel'] = False
|
||||
|
||||
# 9) If status is 'DIS' and all HTLCS are in LOCKED
|
||||
elif order.status == Order.Status.DIS:# TODO Add the other status
|
||||
elif order.status == Order.Status.DIS:
|
||||
|
||||
# add whether the dispute statement has been received
|
||||
if data['is_maker']:
|
||||
|
@ -9,9 +9,7 @@ import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||
import NumbersIcon from '@mui/icons-material/Numbers';
|
||||
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;
|
||||
@ -45,12 +43,13 @@ export default class OrderPage extends Component {
|
||||
total_secs_exp: 300,
|
||||
loading: true,
|
||||
openCancel: false,
|
||||
openCollaborativeCancel: false,
|
||||
};
|
||||
this.orderId = this.props.match.params.orderId;
|
||||
this.getCurrencyDict();
|
||||
this.getOrderDetails();
|
||||
|
||||
// Refresh delais according to Order status
|
||||
// Refresh delays according to Order status
|
||||
this.statusToDelay = {
|
||||
"0": 3000, //'Waiting for maker bond'
|
||||
"1": 30000, //'Public'
|
||||
@ -73,60 +72,16 @@ export default class OrderPage extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
// Unneeded for the most part. Let's keep variable names as they come from the API
|
||||
// Will need some renaming everywhere, but will decrease the mess.
|
||||
completeSetState=(newStateVars)=>{
|
||||
console.log(newStateVars)
|
||||
var otherStateVars = {
|
||||
loading: false,
|
||||
delay: this.setDelay(newStateVars.status),
|
||||
currencyCode: this.getCurrencyCode(newStateVars.currency),
|
||||
};
|
||||
console.log(otherStateVars)
|
||||
var completeStateVars = Object.assign({}, newStateVars, otherStateVars);
|
||||
console.log(completeStateVars)
|
||||
this.setState(completeStateVars);
|
||||
// {
|
||||
// loading: false,
|
||||
// delay: this.setDelay(data.status),
|
||||
// id: data.id,
|
||||
// status: data.status,
|
||||
// status_message: data.status_message,
|
||||
// type: data.type,
|
||||
// currency: data.currency,
|
||||
// currencyCode: this.getCurrencyCode(data.currency),
|
||||
// amount: data.amount,
|
||||
// payment_method: data.payment_method,
|
||||
// isExplicit: data.is_explicit,
|
||||
// premium: data.premium,
|
||||
// satoshis: data.satoshis,
|
||||
// makerId: data.maker,
|
||||
// is_participant: data.is_participant,
|
||||
// urNick: data.ur_nick,
|
||||
// maker_nick: data.maker_nick,
|
||||
// takerId: data.taker,
|
||||
// taker_nick: data.taker_nick,
|
||||
// is_maker: data.is_maker,
|
||||
// is_taker: data.is_taker,
|
||||
// is_buyer: data.is_buyer,
|
||||
// is_seller: data.is_seller,
|
||||
// penalty: data.penalty,
|
||||
// expires_at: data.expires_at,
|
||||
// bad_request: data.bad_request,
|
||||
// bond_invoice: data.bond_invoice,
|
||||
// bondSatoshis: data.bond_satoshis,
|
||||
// escrow_invoice: data.escrow_invoice,
|
||||
// escrowSatoshis: data.escrow_satoshis,
|
||||
// invoice_amount: data.invoice_amount,
|
||||
// total_secs_exp: data.total_secs_exp,
|
||||
// num_similar_orders: data.num_similar_orders,
|
||||
// price_now: data.price_now,
|
||||
// premium_now: data.premium_now,
|
||||
// probots_in_book: data.robots_in_book,
|
||||
// premium_percentile: data.premium_percentile,
|
||||
// num_similar_orders: data.num_similar_orders
|
||||
// })
|
||||
}
|
||||
|
||||
getOrderDetails() {
|
||||
this.setState(null)
|
||||
fetch('/api/order' + '?order_id=' + this.orderId)
|
||||
@ -277,6 +232,53 @@ export default class OrderPage extends Component {
|
||||
)
|
||||
}
|
||||
|
||||
handleClickConfirmCollaborativeCancelButton=()=>{
|
||||
console.log(this.state)
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
|
||||
body: JSON.stringify({
|
||||
'action':'cancel',
|
||||
}),
|
||||
};
|
||||
fetch('/api/order/' + '?order_id=' + this.orderId, requestOptions)
|
||||
.then((response) => response.json())
|
||||
.then((data) => (console.log(data) & this.getOrderDetails(data.id)));
|
||||
this.handleClickCloseCollaborativeCancelDialog();
|
||||
}
|
||||
|
||||
handleClickOpenCollaborativeCancelDialog = () => {
|
||||
this.setState({openCollaborativeCancel: true});
|
||||
};
|
||||
handleClickCloseCollaborativeCancelDialog = () => {
|
||||
this.setState({openCollaborativeCancel: false});
|
||||
};
|
||||
|
||||
CollaborativeCancelDialog =() =>{
|
||||
return(
|
||||
<Dialog
|
||||
open={this.state.openCollaborativeCancel}
|
||||
onClose={this.handleClickCloseCollaborativeCancelDialog}
|
||||
aria-labelledby="collaborative-cancel-dialog-title"
|
||||
aria-describedby="collaborative-cancel-dialog-description"
|
||||
>
|
||||
<DialogTitle id="cancel-dialog-title">
|
||||
{"Collaborative cancel the order?"}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="cancel-dialog-description">
|
||||
The trade escrow has been posted. The order can be cancelled only if both, maker and
|
||||
taker, agree to cancel.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={this.handleClickCloseCollaborativeCancelDialog} autoFocus>Go back</Button>
|
||||
<Button onClick={this.handleClickConfirmCollaborativeCancelButton}> Collaborative Cancel </Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
CancelButton = () => {
|
||||
|
||||
// If maker and Waiting for Bond. Or if taker and Waiting for bond.
|
||||
@ -289,15 +291,25 @@ export default class OrderPage extends Component {
|
||||
)}
|
||||
// If the order does not yet have an escrow deposited. Show dialog
|
||||
// to confirm forfeiting the bond
|
||||
if (this.state.status in [0,1,3,6,7]){
|
||||
if ([1,3,6,7].includes(this.state.status)){
|
||||
return(
|
||||
<div id="openDialogCancelButton">
|
||||
<Grid item xs={12} align="center">
|
||||
<this.CancelDialog/>
|
||||
<Button variant='contained' color='secondary' onClick={this.handleClickOpenConfirmCancelDialog}>Cancel</Button>
|
||||
</Grid>
|
||||
</div>
|
||||
)}
|
||||
|
||||
// TODO If the escrow is Locked, show the collaborative cancel button.
|
||||
// If the escrow is Locked, show the collaborative cancel button.
|
||||
|
||||
if ([8,9].includes(this.state.status)){
|
||||
return(
|
||||
<Grid item xs={12} align="center">
|
||||
<this.CollaborativeCancelDialog/>
|
||||
<Button variant='contained' color='secondary' onClick={this.handleClickOpenCollaborativeCancelDialog}>Collaborative Cancel</Button>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
// If none of the above do not return a cancel button.
|
||||
return(null)
|
||||
@ -414,23 +426,48 @@ export default class OrderPage extends Component {
|
||||
</>
|
||||
: null}
|
||||
|
||||
{/* If the counterparty asked for collaborative cancel */}
|
||||
{this.state.pending_cancel ?
|
||||
<>
|
||||
<Divider />
|
||||
<Grid item xs={12} align="center">
|
||||
<Alert severity="warning" sx={{maxWidth:360}}>
|
||||
{this.state.is_maker ? this.state.taker_nick : this.state.maker_nick} is asking for a collaborative cancel
|
||||
</Alert>
|
||||
</Grid>
|
||||
</>
|
||||
: null}
|
||||
|
||||
{/* If the user has asked for a collaborative cancel */}
|
||||
{this.state.asked_for_cancel ?
|
||||
<>
|
||||
<Divider />
|
||||
<Grid item xs={12} align="center">
|
||||
<Alert severity="warning" sx={{maxWidth:360}}>
|
||||
You asked for a collaborative cancellation
|
||||
</Alert>
|
||||
</Grid>
|
||||
</>
|
||||
: null}
|
||||
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} align="center">
|
||||
{/* Participants can see the "Cancel" Button, but cannot see the "Back" or "Take Order" buttons */}
|
||||
{this.state.is_participant ?
|
||||
<this.CancelButton/>
|
||||
:
|
||||
<>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} align="center">
|
||||
<Button variant='contained' color='primary' onClick={this.handleClickTakeOrderButton}>Take Order</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Button variant='contained' color='secondary' onClick={this.handleClickBackButton}>Back</Button>
|
||||
</Grid>
|
||||
</>
|
||||
</Grid>
|
||||
}
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
@ -89,15 +89,15 @@ export default class TradeBox extends Component {
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
The RoboSats staff will examine the statements and evidence provided by the participants.
|
||||
It is best if you provide a burner contact method on your statement for the staff to contact you.
|
||||
The satoshis in the trade escrow will be sent to the dispute winner, while the dispute
|
||||
loser will lose the bond.
|
||||
The RoboSats staff will examine the statements and evidence provided. You need to build
|
||||
a complete case, as the staff cannot read the chat. It is best to provide a burner contact
|
||||
method with your statement. The satoshis in the trade escrow will be sent to the dispute winner,
|
||||
while the dispute loser will lose the bond.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={this.handleClickCloseConfirmDispute} autoFocus>Disagree</Button>
|
||||
<Button onClick={this.handleClickAgreeDisputeButton}> Agree </Button>
|
||||
<Button onClick={this.handleClickAgreeDisputeButton}> Agree and open dispute </Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user