mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 12:11:35 +00:00
Work on LN bonds. Maker bond works. Yet, this is not the best way probably.
This commit is contained in:
parent
17df987630
commit
8bc8f539d0
1
.gitignore
vendored
1
.gitignore
vendored
@ -643,4 +643,5 @@ frontend/static/frontend/main*
|
||||
frontend/static/assets/avatars*
|
||||
api/lightning/lightning*
|
||||
api/lightning/invoices*
|
||||
api/lightning/router*
|
||||
api/lightning/googleapis*
|
||||
|
@ -1,6 +1,7 @@
|
||||
import grpc, os, hashlib, secrets, json
|
||||
from . import lightning_pb2 as lnrpc, lightning_pb2_grpc as lightningstub
|
||||
from . import invoices_pb2 as invoicesrpc, invoices_pb2_grpc as invoicesstub
|
||||
from . import router_pb2 as routerrpc, router_pb2_grpc as routerstub
|
||||
|
||||
from decouple import config
|
||||
from base64 import b64decode
|
||||
@ -19,10 +20,13 @@ LND_GRPC_HOST = config('LND_GRPC_HOST')
|
||||
class LNNode():
|
||||
|
||||
os.environ["GRPC_SSL_CIPHER_SUITES"] = 'HIGH+ECDSA'
|
||||
|
||||
creds = grpc.ssl_channel_credentials(CERT)
|
||||
channel = grpc.secure_channel(LND_GRPC_HOST, creds)
|
||||
|
||||
lightningstub = lightningstub.LightningStub(channel)
|
||||
invoicesstub = invoicesstub.InvoicesStub(channel)
|
||||
routerstub = routerstub.RouterStub(channel)
|
||||
|
||||
@classmethod
|
||||
def decode_payreq(cls, invoice):
|
||||
@ -46,8 +50,8 @@ class LNNode():
|
||||
@classmethod
|
||||
def settle_hold_invoice(cls, preimage):
|
||||
'''settles a hold invoice'''
|
||||
request = invoicesrpc.SettleInvoiceMsg(preimage=preimage)
|
||||
response = invoicesstub.SettleInvoice(request, metadata=[('macaroon', MACAROON.hex())])
|
||||
request = invoicesrpc.SettleInvoiceMsg(preimage=bytes.fromhex(preimage))
|
||||
response = cls.invoicesstub.SettleInvoice(request, metadata=[('macaroon', MACAROON.hex())])
|
||||
# Fix this: tricky because settling sucessfully an invoice has no response. TODO
|
||||
if response == None:
|
||||
return True
|
||||
@ -84,32 +88,30 @@ class LNNode():
|
||||
@classmethod
|
||||
def validate_hold_invoice_locked(cls, payment_hash):
|
||||
'''Checks if hold invoice is locked'''
|
||||
|
||||
request = invoicesrpc.LookupInvoiceMsg(payment_hash=payment_hash)
|
||||
response = invoicesstub.LookupInvoiceV2(request, metadata=[('macaroon', MACAROON.hex())])
|
||||
|
||||
# What is the state for locked ???
|
||||
if response.state == 'OPEN' or response.state == 'SETTLED':
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
request = invoicesrpc.LookupInvoiceMsg(payment_hash=bytes.fromhex(payment_hash))
|
||||
response = cls.invoicesstub.LookupInvoiceV2(request, metadata=[('macaroon', MACAROON.hex())])
|
||||
print('status here')
|
||||
print(response.state) # LND states: 0 OPEN, 1 SETTLED, 3 ACCEPTED, GRPC_ERROR status 5 when cancelled
|
||||
return response.state == 3 # True if hold invoice is accepted.
|
||||
|
||||
@classmethod
|
||||
def check_until_invoice_locked(cls, payment_hash, expiration):
|
||||
'''Checks until hold invoice is locked.
|
||||
When invoice is locked, returns true.
|
||||
If time expires, return False.'''
|
||||
|
||||
request = invoicesrpc.SubscribeSingleInvoiceRequest(r_hash=payment_hash)
|
||||
for invoice in invoicesstub.SubscribeSingleInvoice(request):
|
||||
# Experimental, needs asyncio
|
||||
# Maybe best to pass LNpayment object and change status live.
|
||||
|
||||
request = cls.invoicesrpc.SubscribeSingleInvoiceRequest(r_hash=payment_hash)
|
||||
for invoice in cls.invoicesstub.SubscribeSingleInvoice(request):
|
||||
print(invoice)
|
||||
if timezone.now > expiration:
|
||||
break
|
||||
if invoice.state == 'LOCKED':
|
||||
if invoice.state == 'ACCEPTED':
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@classmethod
|
||||
def validate_ln_invoice(cls, invoice, num_satoshis):
|
||||
'''Checks if the submited LN invoice comforms to expectations'''
|
||||
@ -125,10 +127,15 @@ class LNNode():
|
||||
|
||||
try:
|
||||
payreq_decoded = cls.decode_payreq(invoice)
|
||||
print(payreq_decoded)
|
||||
except:
|
||||
buyer_invoice['context'] = {'bad_invoice':'Does not look like a valid lightning invoice'}
|
||||
return buyer_invoice
|
||||
|
||||
if payreq_decoded.num_satoshis == 0:
|
||||
buyer_invoice['context'] = {'bad_invoice':'The invoice provided has no explicit amount'}
|
||||
return buyer_invoice
|
||||
|
||||
if not payreq_decoded.num_satoshis == num_satoshis:
|
||||
buyer_invoice['context'] = {'bad_invoice':'The invoice provided is not for '+'{:,}'.format(num_satoshis)+ ' Sats'}
|
||||
return buyer_invoice
|
||||
@ -147,11 +154,28 @@ class LNNode():
|
||||
return buyer_invoice
|
||||
|
||||
@classmethod
|
||||
def pay_invoice(cls, invoice):
|
||||
def pay_invoice(cls, invoice, num_satoshis):
|
||||
'''Sends sats to buyer'''
|
||||
# Needs router subservice
|
||||
# Maybe best to pass order and change status live.
|
||||
|
||||
fee_limit_sat = max(num_satoshis * 0.0002, 10) # 200 ppm or 10 sats
|
||||
|
||||
return True
|
||||
request = routerrpc.SendPaymentRequest(
|
||||
payment_request=invoice,
|
||||
amt_msat=num_satoshis,
|
||||
fee_limit_sat=fee_limit_sat,
|
||||
timeout_seconds=60,
|
||||
)
|
||||
|
||||
for response in routerstub.SendPaymentV2(request, metadata=[('macaroon', MACAROON.hex())]):
|
||||
print(response)
|
||||
print(response.status)
|
||||
|
||||
if response.status == True:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def double_check_htlc_is_settled(cls, payment_hash):
|
||||
|
@ -149,7 +149,6 @@ class Logics():
|
||||
|
||||
# If the order status is 'Waiting for both'. Move forward to 'waiting for escrow'
|
||||
if order.status == Order.Status.WF2:
|
||||
print(order.trade_escrow)
|
||||
if order.trade_escrow:
|
||||
if order.trade_escrow.status == LNPayment.Status.LOCKED:
|
||||
order.status = Order.Status.CHA
|
||||
@ -159,27 +158,36 @@ class Logics():
|
||||
order.save()
|
||||
return True, None
|
||||
|
||||
def add_profile_rating(profile, rating):
|
||||
''' adds a new rating to a user profile'''
|
||||
|
||||
profile.total_ratings = profile.total_ratings + 1
|
||||
latest_ratings = profile.latest_ratings
|
||||
if len(latest_ratings) <= 1:
|
||||
profile.latest_ratings = [rating]
|
||||
profile.avg_rating = rating
|
||||
|
||||
else:
|
||||
latest_ratings = list(latest_ratings).append(rating)
|
||||
profile.latest_ratings = latest_ratings
|
||||
profile.avg_rating = sum(latest_ratings) / len(latest_ratings)
|
||||
|
||||
profile.save()
|
||||
|
||||
@classmethod
|
||||
def rate_counterparty(cls, order, user, rating):
|
||||
|
||||
# If the trade is finished
|
||||
if order.status > Order.Status.PAY:
|
||||
|
||||
# if maker, rates taker
|
||||
if order.maker == user:
|
||||
order.taker.profile.total_ratings = order.taker.profile.total_ratings + 1
|
||||
last_ratings = list(order.taker.profile.last_ratings).append(rating)
|
||||
order.taker.profile.total_ratings = sum(last_ratings) / len(last_ratings)
|
||||
|
||||
cls.add_profile_rating(order.taker.profile, rating)
|
||||
# if taker, rates maker
|
||||
if order.taker == user:
|
||||
order.maker.profile.total_ratings = order.maker.profile.total_ratings + 1
|
||||
last_ratings = list(order.maker.profile.last_ratings).append(rating)
|
||||
order.maker.profile.total_ratings = sum(last_ratings) / len(last_ratings)
|
||||
cls.add_profile_rating(order.maker.profile, rating)
|
||||
else:
|
||||
return False, {'bad_request':'You cannot rate your counterparty yet.'}
|
||||
|
||||
order.save()
|
||||
return True, None
|
||||
|
||||
def is_penalized(user):
|
||||
@ -204,7 +212,7 @@ class Logics():
|
||||
order.maker = None
|
||||
order.status = Order.Status.UCA
|
||||
order.save()
|
||||
return True, None
|
||||
return True, {}
|
||||
|
||||
# 2) When maker cancels after bond
|
||||
'''The order dissapears from book and goes to cancelled.
|
||||
@ -213,12 +221,14 @@ class Logics():
|
||||
of the bond (requires maker submitting an invoice)'''
|
||||
elif order.status == Order.Status.PUB and order.maker == user:
|
||||
#Settle the maker bond (Maker loses the bond for a public order)
|
||||
valid = cls.settle_maker_bond(order)
|
||||
if valid:
|
||||
if cls.settle_maker_bond(order):
|
||||
order.maker_bond.status = LNPayment.Status.SETLED
|
||||
order.maker_bond.save()
|
||||
|
||||
order.maker = None
|
||||
order.status = Order.Status.UCA
|
||||
order.save()
|
||||
return True, None
|
||||
return True, {}
|
||||
|
||||
# 3) When taker cancels before bond
|
||||
''' The order goes back to the book as public.
|
||||
@ -226,13 +236,13 @@ class Logics():
|
||||
elif order.status == Order.Status.TAK and order.taker == user:
|
||||
# adds a timeout penalty
|
||||
user.profile.penalty_expiration = timezone.now() + timedelta(seconds=PENALTY_TIMEOUT)
|
||||
user.save()
|
||||
user.profile.save()
|
||||
|
||||
order.taker = None
|
||||
order.status = Order.Status.PUB
|
||||
order.save()
|
||||
|
||||
return True, None
|
||||
return True, {}
|
||||
|
||||
# 4) When taker or maker cancel after bond (before escrow)
|
||||
'''The order goes into cancelled status if maker cancels.
|
||||
@ -248,19 +258,19 @@ class Logics():
|
||||
order.maker = None
|
||||
order.status = Order.Status.UCA
|
||||
order.save()
|
||||
return True, None
|
||||
return True, {}
|
||||
|
||||
# 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:
|
||||
#Settle the maker bond (Maker loses the bond for canceling an ongoing trade)
|
||||
# Settle the maker bond (Maker loses the bond for canceling an ongoing trade)
|
||||
valid = cls.settle_taker_bond(order)
|
||||
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()
|
||||
return True, None
|
||||
return True, {}
|
||||
|
||||
# 5) When trade collateral has been posted (after escrow)
|
||||
'''Always goes to cancelled status. Collaboration is needed.
|
||||
@ -281,6 +291,7 @@ class Logics():
|
||||
|
||||
# Return the previous invoice if there was one and is still unpaid
|
||||
if order.maker_bond:
|
||||
cls.check_maker_bond_locked(order)
|
||||
if order.maker_bond.status == LNPayment.Status.INVGEN:
|
||||
return True, {'bond_invoice':order.maker_bond.invoice,'bond_satoshis':order.maker_bond.num_satoshis}
|
||||
else:
|
||||
@ -289,7 +300,7 @@ class Logics():
|
||||
order.last_satoshis = cls.satoshis_now(order)
|
||||
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
|
||||
|
||||
description = f"RoboSats - Publishing '{str(order)}' - This is a maker bond. It will automatically return if you do not cancel or cheat"
|
||||
description = f"RoboSats - Publishing '{str(order)}' - This is a maker bond, it will freeze in your wallet. It automatically returns. It will be charged if you cheat or cancel."
|
||||
|
||||
# Gen hold Invoice
|
||||
hold_payment = LNNode.gen_hold_invoice(bond_satoshis, description, BOND_EXPIRY*3600)
|
||||
@ -311,6 +322,29 @@ class Logics():
|
||||
order.save()
|
||||
return True, {'bond_invoice':hold_payment['invoice'], 'bond_satoshis':bond_satoshis}
|
||||
|
||||
@classmethod
|
||||
def check_until_maker_bond_locked(cls, order):
|
||||
expiration = order.maker_bond.created_at + timedelta(seconds=EXP_MAKER_BOND_INVOICE)
|
||||
is_locked = LNNode.check_until_invoice_locked(order.payment_hash, expiration)
|
||||
|
||||
if is_locked:
|
||||
order.maker_bond.status = LNPayment.Status.LOCKED
|
||||
order.maker_bond.save()
|
||||
order.status = Order.Status.PUB
|
||||
|
||||
order.save()
|
||||
return is_locked
|
||||
|
||||
@classmethod
|
||||
def check_maker_bond_locked(cls, order):
|
||||
if LNNode.validate_hold_invoice_locked(order.maker_bond.payment_hash):
|
||||
order.maker_bond.status = LNPayment.Status.LOCKED
|
||||
order.maker_bond.save()
|
||||
order.status = Order.Status.PUB
|
||||
order.save()
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def gen_taker_hold_invoice(cls, order, user):
|
||||
|
||||
@ -330,7 +364,7 @@ class Logics():
|
||||
|
||||
order.last_satoshis = cls.satoshis_now(order) # LOCKS THE AMOUNT OF SATOSHIS FOR THE TRADE
|
||||
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
|
||||
description = f"RoboSats - Taking '{str(order)}' - This is a taker bond. It will automatically return if you do not cancel or cheat"
|
||||
description = f"RoboSats - Taking '{str(order)}' - This is a taker bond, it will freeze in your wallet. It automatically returns. It will be charged if you cheat or cancel."
|
||||
|
||||
# Gen hold Invoice
|
||||
hold_payment = LNNode.gen_hold_invoice(bond_satoshis, description, BOND_EXPIRY*3600)
|
||||
@ -407,12 +441,10 @@ class Logics():
|
||||
def settle_maker_bond(order):
|
||||
''' Settles the maker bond hold invoice'''
|
||||
# TODO ERROR HANDLING
|
||||
valid = LNNode.settle_hold_invoice(order.maker_bond.preimage)
|
||||
if valid:
|
||||
if LNNode.settle_hold_invoice(order.maker_bond.preimage):
|
||||
order.maker_bond.status = LNPayment.Status.SETLED
|
||||
order.save()
|
||||
|
||||
return valid
|
||||
return True
|
||||
|
||||
def settle_taker_bond(order):
|
||||
''' Settles the taker bond hold invoice'''
|
||||
|
@ -34,8 +34,8 @@ class LNPayment(models.Model):
|
||||
RETNED = 3, 'Returned'
|
||||
MISSNG = 4, 'Missing'
|
||||
VALIDI = 5, 'Valid'
|
||||
PAYING = 6, 'Paying ongoing'
|
||||
FAILRO = 7, 'Failed routing'
|
||||
FLIGHT = 6, 'On flight'
|
||||
FAILRO = 7, 'Routing failed'
|
||||
|
||||
# payment use details
|
||||
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False, default=Types.HOLD)
|
||||
|
34
api/views.py
34
api/views.py
@ -60,7 +60,7 @@ class MakerView(CreateAPIView):
|
||||
premium=premium,
|
||||
satoshis=satoshis,
|
||||
is_explicit=is_explicit,
|
||||
expires_at=timezone.now()+timedelta(minutes=EXP_MAKER_BOND_INVOICE), # TODO Move to class method
|
||||
expires_at=timezone.now()+timedelta(seconds=EXP_MAKER_BOND_INVOICE), # TODO Move to class method
|
||||
maker=request.user)
|
||||
|
||||
# TODO move to Order class method when new instance is created!
|
||||
@ -95,11 +95,11 @@ class OrderView(viewsets.ViewSet):
|
||||
# This is our order.
|
||||
order = order[0]
|
||||
|
||||
# 1) If order expired
|
||||
# 1) If order has expired
|
||||
if order.status == Order.Status.EXP:
|
||||
return Response({'bad_request':'This order has expired'},status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 2) If order cancelled
|
||||
# 2) If order has been cancelled
|
||||
if order.status == Order.Status.UCA:
|
||||
return Response({'bad_request':'This order has been cancelled by the maker'},status.HTTP_400_BAD_REQUEST)
|
||||
if order.status == Order.Status.CCA:
|
||||
@ -107,7 +107,7 @@ class OrderView(viewsets.ViewSet):
|
||||
|
||||
data = ListOrderSerializer(order).data
|
||||
|
||||
# if user is under a limit (penalty), inform him
|
||||
# if user is under a limit (penalty), inform him.
|
||||
is_penalized, time_out = Logics.is_penalized(request.user)
|
||||
if is_penalized:
|
||||
data['penalty'] = time_out
|
||||
@ -125,7 +125,7 @@ class OrderView(viewsets.ViewSet):
|
||||
elif not data['is_participant'] and order.status != Order.Status.PUB:
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
|
||||
# For participants add position side, nicks and status as message
|
||||
# For participants add positions, nicks and status as a message
|
||||
data['is_buyer'] = Logics.is_buyer(order,request.user)
|
||||
data['is_seller'] = Logics.is_seller(order,request.user)
|
||||
data['maker_nick'] = str(order.maker)
|
||||
@ -134,7 +134,7 @@ class OrderView(viewsets.ViewSet):
|
||||
data['is_fiat_sent'] = order.is_fiat_sent
|
||||
data['is_disputed'] = order.is_disputed
|
||||
|
||||
# If both bonds are locked, participants can see the trade in sats is also final.
|
||||
# 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 pays
|
||||
@ -182,8 +182,10 @@ class OrderView(viewsets.ViewSet):
|
||||
else:
|
||||
return Response(context, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 8) If status is 'CHA'or '' or '' and all HTLCS are in LOCKED
|
||||
elif order.status == Order.Status.CHA: # TODO Add the other status
|
||||
# 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
|
||||
|
||||
# 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
|
||||
@ -193,7 +195,7 @@ class OrderView(viewsets.ViewSet):
|
||||
|
||||
def take_update_confirm_dispute_cancel(self, request, format=None):
|
||||
'''
|
||||
Here take place all of the user updates to the order object.
|
||||
Here takes place all of updatesto the order object.
|
||||
That is: take, confim, cancel, dispute, update_invoice or rate.
|
||||
'''
|
||||
order_id = request.GET.get(self.lookup_url_kwarg)
|
||||
@ -208,7 +210,7 @@ class OrderView(viewsets.ViewSet):
|
||||
invoice = serializer.data.get('invoice')
|
||||
rating = serializer.data.get('rating')
|
||||
|
||||
# 1) If action is take, it is be taker request!
|
||||
# 1) If action is take, it is a taker request!
|
||||
if action == 'take':
|
||||
if order.status == Order.Status.PUB:
|
||||
valid, context = Logics.validate_already_maker_or_taker(request.user)
|
||||
@ -253,7 +255,7 @@ class OrderView(viewsets.ViewSet):
|
||||
return Response(
|
||||
{'bad_request':
|
||||
'The Robotic Satoshis working in the warehouse did not understand you. ' +
|
||||
'Please, fill a Bug Issue in Github https://github.com/Reckless-Satoshi/robosats/issues'},
|
||||
'Please, fill a Bug Issue in Github https://github.com/reckless-satoshi/robosats/issues'},
|
||||
status.HTTP_501_NOT_IMPLEMENTED)
|
||||
|
||||
return self.get(request)
|
||||
@ -277,6 +279,16 @@ class UserView(APIView):
|
||||
- Creates login credentials (new User object)
|
||||
Response with Avatar and Nickname.
|
||||
'''
|
||||
|
||||
# if request.user.id:
|
||||
# context = {}
|
||||
# context['nickname'] = request.user.username
|
||||
# participant = not Logics.validate_already_maker_or_taker(request.user)
|
||||
# context['bad_request'] = f'You are already logged in as {request.user}'
|
||||
# if participant:
|
||||
# context['bad_request'] = f'You are already logged in as as {request.user} and have an active order'
|
||||
# return Response(context,status.HTTP_200_OK)
|
||||
|
||||
token = request.GET.get(self.lookup_url_kwarg)
|
||||
|
||||
# Compute token entropy
|
||||
|
@ -67,7 +67,7 @@ export default class OrderPage extends Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
isExplicit: false,
|
||||
delay: 10000, // Refresh every 10 seconds
|
||||
delay: 2000, // Refresh every 2 seconds by default
|
||||
currencies_dict: {"1":"USD"}
|
||||
};
|
||||
this.orderId = this.props.match.params.orderId;
|
||||
@ -109,7 +109,7 @@ export default class OrderPage extends Component {
|
||||
escrowInvoice: data.escrow_invoice,
|
||||
escrowSatoshis: data.escrow_satoshis,
|
||||
invoiceAmount: data.invoice_amount,
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@ -129,9 +129,6 @@ export default class OrderPage extends Component {
|
||||
tick = () => {
|
||||
this.getOrderDetails();
|
||||
}
|
||||
handleDelayChange = (e) => {
|
||||
this.setState({ delay: Number(e.target.value) });
|
||||
}
|
||||
|
||||
// Fix to use proper react props
|
||||
handleClickBackButton=()=>{
|
||||
@ -149,7 +146,9 @@ 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)));
|
||||
.then((data) => (this.setState({badRequest:data.bad_request})
|
||||
& console.log(data)
|
||||
& this.getOrderDetails(data.id)));
|
||||
}
|
||||
getCurrencyDict() {
|
||||
fetch('/static/assets/currencies.json')
|
||||
@ -278,8 +277,9 @@ export default class OrderPage extends Component {
|
||||
</>
|
||||
}
|
||||
|
||||
{/* Makers can cancel before commiting the bond (status 0)*/}
|
||||
{this.state.isMaker & this.state.statusCode == 0 ?
|
||||
{/* 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>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { Component } from "react";
|
||||
import { Paper, Button, Grid, Typography, TextField, List, ListItem, ListItemText, Divider} from "@mui/material"
|
||||
import { Link, Paper, Rating, Button, Grid, Typography, TextField, List, ListItem, ListItemText, Divider} from "@mui/material"
|
||||
import QRCode from "react-qr-code";
|
||||
|
||||
function getCookie(name) {
|
||||
@ -294,6 +294,19 @@ handleClickOpenDisputeButton=()=>{
|
||||
.then((response) => response.json())
|
||||
.then((data) => (this.props.data = data));
|
||||
}
|
||||
handleRatingChange=(e)=>{
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type':'application/json', 'X-CSRFToken': getCookie('csrftoken'),},
|
||||
body: JSON.stringify({
|
||||
'action': "rate",
|
||||
'rating': e.target.value,
|
||||
}),
|
||||
};
|
||||
fetch('/api/order/' + '?order_id=' + this.props.data.id, requestOptions)
|
||||
.then((response) => response.json())
|
||||
.then((data) => (this.props.data = data));
|
||||
}
|
||||
|
||||
showFiatSentButton(){
|
||||
return(
|
||||
@ -359,6 +372,7 @@ handleClickOpenDisputeButton=()=>{
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// showFiatReceivedButton(){
|
||||
|
||||
// }
|
||||
@ -367,9 +381,28 @@ handleClickOpenDisputeButton=()=>{
|
||||
|
||||
// }
|
||||
|
||||
// showRateSelect(){
|
||||
|
||||
// }
|
||||
showRateSelect(){
|
||||
return(
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h6" variant="h6">
|
||||
🎉Trade finished!🥳
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="body2" variant="body2" align="center">
|
||||
What do you think of <b>{this.props.data.isMaker ? this.props.data.takerNick : this.props.data.makerNick}</b>?
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Rating name="size-large" defaultValue={2} size="large" onChange={this.handleRatingChange} />
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Button color='primary' to='/' component={Link}>Start Again</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
@ -393,14 +426,25 @@ handleClickOpenDisputeButton=()=>{
|
||||
{this.props.data.isBuyer & this.props.data.statusCode == 7 ? this.showWaitingForEscrow() : ""}
|
||||
{this.props.data.isSeller & this.props.data.statusCode == 8 ? this.showWaitingForBuyerInvoice() : ""}
|
||||
|
||||
{/* In Chatroom - showChat(showSendButton, showReveiceButton, showDisputeButton) */}
|
||||
{/* In Chatroom - No fiat sent - showChat(showSendButton, showReveiceButton, showDisputeButton) */}
|
||||
{this.props.data.isBuyer & this.props.data.statusCode == 9 ? this.showChat(true,false,true) : ""}
|
||||
{this.props.data.isSeller & this.props.data.statusCode == 9 ? this.showChat(false,false,true) : ""}
|
||||
|
||||
{/* In Chatroom - Fiat sent - showChat(showSendButton, showReveiceButton, showDisputeButton) */}
|
||||
{this.props.data.isBuyer & this.props.data.statusCode == 10 ? this.showChat(false,false,true) : ""}
|
||||
{this.props.data.isSeller & this.props.data.statusCode == 10 ? this.showChat(false,true,true) : ""}
|
||||
|
||||
{/* Trade Finished */}
|
||||
{this.props.data.isSeller & this.props.data.statusCode > 12 & this.props.data.statusCode < 15 ? this.showRateSelect() : ""}
|
||||
{this.props.data.isBuyer & this.props.data.statusCode == 14 ? this.showRateSelect() : ""}
|
||||
|
||||
{/* Trade Finished - Payment Routing Failed */}
|
||||
{this.props.data.isBuyer & this.props.data.statusCode == 15 ? this.showUpdateInvoice() : ""}
|
||||
|
||||
{/* Trade Finished - Payment Routing Failed - TODO Needs more planning */}
|
||||
{this.props.data.statusCode == 11 ? this.showInDispute() : ""}
|
||||
|
||||
|
||||
{/* TODO */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
|
12
setup.md
12
setup.md
@ -58,15 +58,17 @@ git clone https://github.com/googleapis/googleapis.git
|
||||
curl -o lightning.proto -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/lightning.proto
|
||||
python3 -m grpc_tools.protoc --proto_path=googleapis:. --python_out=. --grpc_python_out=. lightning.proto
|
||||
```
|
||||
We also use the *Invoices* subservice for invoice validation.
|
||||
We also use the *Invoices* and *Router* subservices for invoice validation and payment routing.
|
||||
```
|
||||
curl -o invoices.proto -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/invoicesrpc/invoices.proto
|
||||
python3 -m grpc_tools.protoc --proto_path=googleapis:. --python_out=. --grpc_python_out=. invoices.proto
|
||||
curl -o router.proto -s https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/routerrpc/router.proto
|
||||
python3 -m grpc_tools.protoc --proto_path=googleapis:. --python_out=. --grpc_python_out=. router.proto
|
||||
```
|
||||
Relative imports are not working at the moment, so some editing is needed in
|
||||
`api/lightning` files `lightning_pb2_grpc.py`, `invoices_pb2_grpc.py` and `invoices_pb2.py`.
|
||||
`api/lightning` files `lightning_pb2_grpc.py`, `invoices_pb2_grpc.py`, `invoices_pb2.py`, `router_pb2_grpc.py` and `router_pb2.py`.
|
||||
|
||||
Example, change line :
|
||||
For example in `lightning_pb2_grpc.py` , add "from . " :
|
||||
|
||||
`import lightning_pb2 as lightning__pb2`
|
||||
|
||||
@ -74,6 +76,8 @@ to
|
||||
|
||||
`from . import lightning_pb2 as lightning__pb2`
|
||||
|
||||
Same for every other file
|
||||
|
||||
## React development environment
|
||||
### Install npm
|
||||
`sudo apt install npm`
|
||||
@ -96,7 +100,7 @@ npm install react-native-svg
|
||||
npm install react-qr-code
|
||||
npm install @mui/material
|
||||
```
|
||||
Note we are using mostly MaterialUI V5, but Image loading from V4 extentions (so both V4 and V5 are needed)
|
||||
Note we are using mostly MaterialUI V5 (@mui/material) but Image loading from V4 (@material-ui/core) extentions (so both V4 and V5 are needed)
|
||||
|
||||
### Launch the React render
|
||||
from frontend/ directory
|
||||
|
Loading…
Reference in New Issue
Block a user