diff --git a/api/admin.py b/api/admin.py index 5d3be217..d8571828 100644 --- a/api/admin.py +++ b/api/admin.py @@ -70,7 +70,7 @@ class OrderAdmin(AdminChangeLinksMixin, admin.ModelAdmin): "taker_bond", "trade_escrow", ) - list_filter = ("is_disputed", "is_fiat_sent", "type", "currency", "status") + list_filter = ("is_disputed", "is_fiat_sent", "is_swap","type", "currency", "status") search_fields = ["id","amount","min_amount","max_amount"] def amt(self, obj): diff --git a/api/logics.py b/api/logics.py index ffd91cad..2a264972 100644 --- a/api/logics.py +++ b/api/logics.py @@ -293,6 +293,10 @@ class Logics: Order.Status.MLD, ] + # in any case, if order is_swap and there is an onchain_payment, cancel it. + if not order.status in does_not_expire: + cls.cancel_onchain_payment(order) + if order.status in does_not_expire: return False @@ -600,7 +604,7 @@ class Logics: 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 a SWAP" + context["swap_failure_reason"] = "Not enough onchain liquidity available to offer a swap" return True, context context["swap_allowed"] = True @@ -720,9 +724,7 @@ class Logics: } # cancel onchain_payout if existing - if order.payout_tx: - order.payout_tx.status = OnchainPayment.Status.CANCE - order.payout_tx.save() + cls.cancel_onchain_payment(order) num_satoshis = cls.payout_amount(order, user)[1]["invoice_amount"] payout = LNNode.validate_ln_invoice(invoice, num_satoshis) @@ -894,9 +896,12 @@ class Logics: # 4.a) When maker cancel after bond (before escrow) """The order into cancelled status if maker cancels.""" elif (order.status in [Order.Status.WF2,Order.Status.WFE] and order.maker == user): + # cancel onchain payment if existing + cls.cancel_onchain_payment(order) # Settle the maker bond (Maker loses the bond for canceling an ongoing trade) valid = cls.settle_bond(order.maker_bond) cls.return_bond(order.taker_bond) # returns taker bond + if valid: order.status = Order.Status.UCA order.save() @@ -905,9 +910,11 @@ class Logics: return True, None # 4.b) When taker cancel after bond (before escrow) - """The order into cancelled status if maker cancels.""" + """The order into cancelled status if mtker cancels.""" elif (order.status in [Order.Status.WF2, Order.Status.WFE] and order.taker == user): + # cancel onchain payment if existing + cls.cancel_onchain_payment(order) # Settle the maker bond (Maker loses the bond for canceling an ongoing trade) valid = cls.settle_bond(order.taker_bond) if valid: @@ -957,6 +964,8 @@ class Logics: def collaborative_cancel(cls, order): if not order.status in [Order.Status.WFI, Order.Status.CHA]: return + # cancel onchain payment if existing + cls.cancel_onchain_payment(order) cls.return_bond(order.maker_bond) cls.return_bond(order.taker_bond) cls.return_escrow(order) @@ -1300,6 +1309,16 @@ class Logics: else: raise e + def cancel_onchain_payment(order): + ''' Cancel onchain_payment if existing ''' + + if order.payout_tx: + order.payout_tx.status = OnchainPayment.Status.CANCE + order.payout_tx.save() + return True + else: + return False + def cancel_bond(bond): """cancel a bond""" # Same as return bond, but used when the invoice was never LOCKED diff --git a/api/models.py b/api/models.py index c1fde799..dc4ec81a 100644 --- a/api/models.py +++ b/api/models.py @@ -251,12 +251,7 @@ class OnchainPayment(models.Model): default=None) def __str__(self): - if self.txid: - txname = str(self.txid)[:8] - else: - txname = str(self.id) - - return f"TX-{txname}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}" + return f"TX-{str(self.id)}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}" class Meta: verbose_name = "Onchain payment" diff --git a/api/tasks.py b/api/tasks.py index f3fde198..98d91205 100644 --- a/api/tasks.py +++ b/api/tasks.py @@ -167,15 +167,17 @@ def follow_send_payment(hash): context = {"routing_failed": "The payout invoice has expired"} return False, context -@shared_task(name="lnpayments_cleansing") -def lnpayments_cleansing(): +@shared_task(name="payments_cleansing") +def payments_cleansing(): """ - Deletes cancelled lnpayments (hodl invoices never locked) that - belong to orders expired more than 3 days ago + Deletes cancelled payments (hodl invoices never locked) that + belong to orders expired more than 3 days ago. + Deletes cancelled onchain_payments """ from django.db.models import Q from api.models import LNPayment + from api.models import OnchainPayment from datetime import timedelta from django.utils import timezone @@ -191,17 +193,35 @@ def lnpayments_cleansing(): # And do not have an active trade, any past contract or any reward. deleted_lnpayments = [] for lnpayment in queryset: - # Try an except. In case some chatroom is already missing. + # Try and except. In case some payment is already missing. try: name = str(lnpayment) lnpayment.delete() deleted_lnpayments.append(name) except: pass + + # same for onchain payments + queryset = OnchainPayment.objects.filter(Q(status=OnchainPayment.Status.CANCEL), + Q(order_paid_TX__expires_at__lt=finished_time)) + + + # And do not have an active trade, any past contract or any reward. + deleted_onchainpayments = [] + for onchainpayment in queryset: + # Try and except. In case some payment is already missing. + try: + name = str(onchainpayment) + onchainpayment.delete() + deleted_onchainpayments.append(name) + except: + pass results = { - "num_deleted": len(deleted_lnpayments), + "num_lnpayments_deleted": len(deleted_lnpayments), "deleted_lnpayments": deleted_lnpayments, + "num_onchainpayments_deleted": len(deleted_onchainpayments), + "deleted_onchainpayments": deleted_onchainpayments, } return results diff --git a/api/views.py b/api/views.py index 211be71a..c991b3b6 100644 --- a/api/views.py +++ b/api/views.py @@ -572,13 +572,13 @@ class UserView(APIView): # If an existing user opens the main page by mistake, we do not want it to create a new nickname/profile for him if request.user.is_authenticated: context = {"nickname": request.user.username} - not_participant, _, _ = Logics.validate_already_maker_or_taker( + not_participant, _, order = Logics.validate_already_maker_or_taker( request.user) # Does not allow this 'mistake' if an active order if not not_participant: - context[ - "bad_request"] = f"You are already logged in as {request.user} and have an active order" + context["active_order_id"] = order.id + context["bad_request"] = f"You are already logged in as {request.user} and have an active order" return Response(context, status.HTTP_400_BAD_REQUEST) # Deprecated, kept temporarily for legacy reasons @@ -643,13 +643,13 @@ class UserView(APIView): # If an existing user opens the main page by mistake, we do not want it to create a new nickname/profile for him if request.user.is_authenticated: context = {"nickname": request.user.username} - not_participant, _, _ = Logics.validate_already_maker_or_taker( + not_participant, _, order = Logics.validate_already_maker_or_taker( request.user) # Does not allow this 'mistake' if an active order if not not_participant: - context[ - "bad_request"] = f"You are already logged in as {request.user} and have an active order" + context["active_order_id"] = order.id + context["bad_request"] = f"You are already logged in as {request.user} and have an active order" return Response(context, status.HTTP_400_BAD_REQUEST) # The new way. The token is never sent. Only its SHA256 @@ -748,6 +748,16 @@ class UserView(APIView): login(request, user) context["public_key"] = user.profile.public_key context["encrypted_private_key"] = user.profile.encrypted_private_key + + # return active order or last made order if any + has_no_active_order, _, order = Logics.validate_already_maker_or_taker(request.user) + if not has_no_active_order: + context["active_order_id"] = order.id + else: + last_order = Order.objects.filter(Q(maker=request.user) | Q(taker=request.user)).last() + if last_order: + context["last_order_id"] = last_order.id + # Sends the welcome back message, only if created +3 mins ago if request.user.date_joined < (timezone.now() - timedelta(minutes=3)): context["found"] = "We found your Robot avatar. Welcome back!" diff --git a/frontend/src/components/BottomBar.js b/frontend/src/components/BottomBar.js index 37ad7da9..e24dd407 100644 --- a/frontend/src/components/BottomBar.js +++ b/frontend/src/components/BottomBar.js @@ -64,9 +64,10 @@ class BottomBar extends Component { fetch('/api/info/') .then((response) => response.json()) .then((data) => this.setState(data) - & this.setState({active_order_id: data.active_order_id ? data.active_order_id : null, - last_order_id: data.last_order_id ? data.last_order_id : null}) - & this.props.setAppState({nickname:data.nickname, loading:false})); + & this.props.setAppState({nickname:data.nickname, + loading:false, + activeOrderId: data.active_order_id ? data.active_order_id : null, + lastOrderId: data.last_order_id ? data.last_order_id : null})); } handleClickOpenStatsForNerds = () => { @@ -129,7 +130,7 @@ class BottomBar extends Component { bottomBarDesktop =()=>{ const { t } = this.props; var hasRewards = this.state.earned_rewards > 0 ? true: false; - var hasOrder = this.state.active_order_id > 0 & !this.state.profileShown & this.props.avatarLoaded ? true : false; + var hasOrder = this.props.activeOrderId > 0 & !this.state.profileShown & this.props.avatarLoaded ? true : false; return( @@ -144,7 +145,7 @@ bottomBarDesktop =()=>{ (hasOrder ? t("You have an active order"):"")} > - 0 & !this.state.profileShown) ? "": null} color="primary"> + 0 & !this.props.profileShown) ? "": null} color="primary"> { - this.props.setAppState({buyChecked: false, sellChecked: true, type:0}) & this.getInfo()} to={`/book/`} @@ -183,6 +185,7 @@ bottomBarDesktop =()=>{ this.props.setAppState({buyChecked: true, sellChecked: false, type:1}) & this.getInfo()} to={`/book/`} @@ -201,7 +204,9 @@ bottomBarDesktop =()=>{ - this.getInfo()} to={`/`} component={LinkRouter} > @@ -350,6 +355,7 @@ bottomBarPhone =()=>{ this.props.setAppState({buyChecked: false, sellChecked: true, type:0}) & this.getInfo()} to={`/book/`} @@ -364,6 +370,7 @@ bottomBarPhone =()=>{ this.props.setAppState({buyChecked: true, sellChecked: false, type:1}) & this.getInfo()} to={`/book/`} @@ -377,7 +384,9 @@ bottomBarPhone =()=>{ - this.getInfo()} to={`/`} component={LinkRouter} > @@ -453,8 +462,8 @@ bottomBarPhone =()=>{ isOpen={this.state.openProfile} handleClickCloseProfile={this.handleClickCloseProfile} nickname={this.props.nickname} - activeOrderId={this.state.active_order_id} - lastOrderId={this.state.last_order_id} + activeOrderId={this.props.activeOrderId} + lastOrderId={this.props.lastOrderId} referralCode={this.state.referral_code} handleSubmitInvoiceClicked={this.handleSubmitInvoiceClicked} host={this.getHost()} diff --git a/frontend/src/components/HomePage.js b/frontend/src/components/HomePage.js index c83301e8..834537b6 100644 --- a/frontend/src/components/HomePage.js +++ b/frontend/src/components/HomePage.js @@ -22,6 +22,8 @@ export default class HomePage extends Component { bookCurrencyCode:'ANY', bookOrders:new Array(), bookLoading: true, + activeOrderId: null, + lastOrderId: null, } } diff --git a/frontend/src/components/UserGenPage.js b/frontend/src/components/UserGenPage.js index 0a0d5508..47e30cbc 100644 --- a/frontend/src/components/UserGenPage.js +++ b/frontend/src/components/UserGenPage.js @@ -91,13 +91,17 @@ class UserGenPage extends Component { // Add nick and token to App state (token only if not a bad request) (data.bad_request ? this.props.setAppState({ nickname: data.nickname, - avatarLoaded: false, + avatarLoaded: false, + activeOrderId: data.active_order_id ? data.active_order_id : null, + lastOrderId: data.last_order_id ? data.last_order_id : null, }) : (this.props.setAppState({ nickname: data.nickname, token: token, avatarLoaded: false, + activeOrderId: data.active_order_id ? data.active_order_id : null, + lastOrderId: data.last_order_id ? data.last_order_id : null, })) & writeCookie("robot_token",token) & writeCookie("pub_key",data.public_key.split('\n').join('\\')) & writeCookie("enc_priv_key",data.encrypted_private_key.split('\n').join('\\'))) @@ -175,7 +179,7 @@ class UserGenPage extends Component {
- {this.state.nickname ? + {this.state.nickname && getCookie("sessionid") ? @@ -185,11 +189,12 @@ class UserGenPage extends Component {
-

diff --git a/frontend/src/utils/cookies.js b/frontend/src/utils/cookies.js index f187e8da..03b28e1c 100644 --- a/frontend/src/utils/cookies.js +++ b/frontend/src/utils/cookies.js @@ -15,7 +15,7 @@ export const getCookie = (name) => { }; export const writeCookie = (key,value) => { - document.cookie=`${key}=${value};path=/`; + document.cookie=`${key}=${value};path=/;SameSite=Strict`; } export const deleteCookie = (name) => {