mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 20:21:35 +00:00
Merge pull request #54 from Reckless-Satoshi/dispute-system-v2
A collection of fixes and small improvements.
This commit is contained in:
commit
bd97148132
@ -58,6 +58,9 @@ PENALTY_TIMEOUT = 60
|
||||
# Time between routing attempts of buyer invoice in MINUTES
|
||||
RETRY_TIME = 5
|
||||
|
||||
# Platform activity limits
|
||||
MAX_PUBLIC_ORDERS = 100
|
||||
|
||||
# Trade limits in satoshis
|
||||
MIN_TRADE = 10000
|
||||
MAX_TRADE = 500000
|
||||
|
@ -103,6 +103,7 @@ class UserProfileAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
||||
"avatar_tag",
|
||||
"id",
|
||||
"user_link",
|
||||
"telegram_enabled",
|
||||
"total_contracts",
|
||||
"platform_rating",
|
||||
"total_ratings",
|
||||
|
@ -4,7 +4,7 @@ from api.lightning.node import LNNode
|
||||
from django.db.models import Q
|
||||
|
||||
from api.models import Order, LNPayment, MarketTick, User, Currency
|
||||
from api.messages import Telegram
|
||||
from api.tasks import send_message
|
||||
from decouple import config
|
||||
|
||||
import math
|
||||
@ -30,7 +30,6 @@ FIAT_EXCHANGE_DURATION = int(config("FIAT_EXCHANGE_DURATION"))
|
||||
|
||||
|
||||
class Logics:
|
||||
telegram = Telegram()
|
||||
@classmethod
|
||||
def validate_already_maker_or_taker(cls, user):
|
||||
"""Validates if a use is already not part of an active order"""
|
||||
@ -129,7 +128,7 @@ class Logics:
|
||||
order.expires_at = timezone.now() + timedelta(
|
||||
seconds=Order.t_to_expire[Order.Status.TAK])
|
||||
order.save()
|
||||
cls.telegram.order_taken(order)
|
||||
send_message.delay(order.id,'order_taken')
|
||||
return True, None
|
||||
|
||||
def is_buyer(order, user):
|
||||
@ -208,11 +207,13 @@ class Logics:
|
||||
cls.return_bond(order.maker_bond)
|
||||
order.status = Order.Status.EXP
|
||||
order.save()
|
||||
send_message.delay(order.id,'order_expired_untaken')
|
||||
return True
|
||||
|
||||
elif order.status == Order.Status.TAK:
|
||||
cls.cancel_bond(order.taker_bond)
|
||||
cls.kick_taker(order)
|
||||
send_message.delay(order.id,'taker_expired_b4bond')
|
||||
return True
|
||||
|
||||
elif order.status == Order.Status.WF2:
|
||||
@ -342,13 +343,18 @@ class Logics:
|
||||
if not order.status == Order.Status.DIS:
|
||||
return False, {
|
||||
"bad_request":
|
||||
"Only orders in dispute accept a dispute statements"
|
||||
"Only orders in dispute accept dispute statements"
|
||||
}
|
||||
|
||||
if len(statement) > 5000:
|
||||
return False, {
|
||||
"bad_statement": "The statement is longer than 5000 characters"
|
||||
}
|
||||
|
||||
if len(statement) < 100:
|
||||
return False, {
|
||||
"bad_statement": "The statement is too short. Make sure to be thorough."
|
||||
}
|
||||
|
||||
if order.maker == user:
|
||||
order.maker_statement = statement
|
||||
@ -356,7 +362,7 @@ class Logics:
|
||||
order.taker_statement = statement
|
||||
|
||||
# If both statements are in, move status to wait for dispute resolution
|
||||
if order.maker_statement != None and order.taker_statement != None:
|
||||
if order.maker_statement not in [None,""] and order.taker_statement not in [None,""]:
|
||||
order.status = Order.Status.WFR
|
||||
order.expires_at = timezone.now() + timedelta(
|
||||
seconds=Order.t_to_expire[Order.Status.WFR])
|
||||
@ -519,11 +525,11 @@ class Logics:
|
||||
to prevent DDOS on the LN node and order book. If not strict, maker is returned
|
||||
the bond (more user friendly)."""
|
||||
elif order.status == Order.Status.PUB and order.maker == user:
|
||||
# Settle the maker bond (Maker loses the bond for cancelling public order)
|
||||
if cls.return_bond(order.maker_bond
|
||||
): # strict: cls.settle_bond(order.maker_bond):
|
||||
# Return the maker bond (Maker gets returned the bond for cancelling public order)
|
||||
if cls.return_bond(order.maker_bond): # strict cancellation: cls.settle_bond(order.maker_bond):
|
||||
order.status = Order.Status.UCA
|
||||
order.save()
|
||||
send_message.delay(order.id,'public_order_cancelled')
|
||||
return True, None
|
||||
|
||||
# 3) When taker cancels before bond
|
||||
@ -533,6 +539,7 @@ class Logics:
|
||||
# adds a timeout penalty
|
||||
cls.cancel_bond(order.taker_bond)
|
||||
cls.kick_taker(order)
|
||||
send_message.delay(order.id,'taker_canceled_b4bond')
|
||||
return True, None
|
||||
|
||||
# 4) When taker or maker cancel after bond (before escrow)
|
||||
@ -612,6 +619,7 @@ class Logics:
|
||||
order.expires_at = order.created_at + timedelta(
|
||||
seconds=Order.t_to_expire[Order.Status.PUB])
|
||||
order.save()
|
||||
send_message.delay(order.id,'order_published')
|
||||
return
|
||||
|
||||
@classmethod
|
||||
@ -999,14 +1007,9 @@ class Logics:
|
||||
order.payout.status = LNPayment.Status.FLIGHT
|
||||
order.payout.save()
|
||||
order.save()
|
||||
send_message.delay(order.id,'trade_successful')
|
||||
return True, None
|
||||
# is_payed, context = follow_send_payment(order.payout) ##### !!! KEY LINE - PAYS THE BUYER INVOICE !!!
|
||||
# if is_payed:
|
||||
# order.save()
|
||||
# return True, context
|
||||
# else:
|
||||
# # error handling here
|
||||
# return False, context
|
||||
|
||||
else:
|
||||
return False, {
|
||||
"bad_request":
|
||||
|
@ -31,7 +31,12 @@ class Command(BaseCommand):
|
||||
if len(list(response['result'])) == 0:
|
||||
continue
|
||||
for result in response['result']:
|
||||
text = result['message']['text']
|
||||
|
||||
try: # if there is no key message, skips this result.
|
||||
text = result['message']['text']
|
||||
except:
|
||||
continue
|
||||
|
||||
splitted_text = text.split(' ')
|
||||
if splitted_text[0] == '/start':
|
||||
token = splitted_text[-1]
|
||||
|
122
api/messages.py
122
api/messages.py
@ -2,6 +2,7 @@ from decouple import config
|
||||
from secrets import token_urlsafe
|
||||
from api.models import Order
|
||||
from api.utils import get_tor_session
|
||||
import time
|
||||
|
||||
class Telegram():
|
||||
''' Simple telegram messages by requesting to API'''
|
||||
@ -33,23 +34,33 @@ class Telegram():
|
||||
chat_id = user.profile.telegram_chat_id
|
||||
message_url = f'https://api.telegram.org/bot{bot_token}/sendMessage?chat_id={chat_id}&text={text}'
|
||||
|
||||
response = self.session.get(message_url).json()
|
||||
print(response)
|
||||
|
||||
return
|
||||
# telegram messaging is atm inserted dangerously in the logics module
|
||||
# if it fails, it should keep trying
|
||||
while True:
|
||||
try:
|
||||
self.session.get(message_url).json()
|
||||
return
|
||||
except:
|
||||
pass
|
||||
|
||||
def welcome(self, user):
|
||||
lang = user.profile.telegram_lang_code
|
||||
order = Order.objects.get(maker=user)
|
||||
|
||||
# In weird cases the order cannot be found (e.g. it is cancelled)
|
||||
|
||||
queryset = Order.objects.filter(maker=user)
|
||||
order = queryset.last()
|
||||
|
||||
print(str(order.id))
|
||||
if lang == 'es':
|
||||
text = f'Hola ⚡{user.username}⚡, Te enviaré un mensaje cuando tu orden con ID {str(order.id)} haya sido tomada.'
|
||||
text = f'Hola {user.username}, te enviaré un mensaje cuando tu orden con ID {str(order.id)} haya sido tomada.'
|
||||
else:
|
||||
text = f"Hey ⚡{user.username}⚡, I will send you a message when someone takes your order with ID {str(order.id)}."
|
||||
text = f"Hey {user.username}, I will send you a message when someone takes your order with ID {str(order.id)}."
|
||||
self.send_message(user, text)
|
||||
user.profile.telegram_welcomed = True
|
||||
user.profile.save()
|
||||
return
|
||||
|
||||
|
||||
def order_taken(self, order):
|
||||
user = order.maker
|
||||
if not user.profile.telegram_enabled:
|
||||
@ -59,9 +70,102 @@ class Telegram():
|
||||
taker_nick = order.taker.username
|
||||
site = config('HOST_NAME')
|
||||
if lang == 'es':
|
||||
text = f'Tu orden con ID {order.id} ha sido tomada por {taker_nick}!🥳 Visita http://{site}/order/{order.id} para continuar.'
|
||||
text = f'¡Tu orden con ID {order.id} ha sido tomada por {taker_nick}!🥳 Visita http://{site}/order/{order.id} para continuar.'
|
||||
else:
|
||||
text = f'Your order with ID {order.id} was taken by {taker_nick}!🥳 Visit http://{site}/order/{order.id} to proceed with the trade.'
|
||||
|
||||
self.send_message(user, text)
|
||||
return
|
||||
|
||||
def order_expired_untaken(self, order):
|
||||
user = order.maker
|
||||
if not user.profile.telegram_enabled:
|
||||
return
|
||||
|
||||
lang = user.profile.telegram_lang_code
|
||||
site = config('HOST_NAME')
|
||||
if lang == 'es':
|
||||
text = f'Tu orden con ID {order.id} ha expirado sin ser tomada por ningún robot. Visita http://{site} para crear una nueva.'
|
||||
else:
|
||||
text = f'Your order with ID {order.id} has expired untaken. Visit http://{site} to create a new one.'
|
||||
|
||||
self.send_message(user, text)
|
||||
return
|
||||
|
||||
def trade_successful(self, order):
|
||||
user = order.maker
|
||||
if not user.profile.telegram_enabled:
|
||||
return
|
||||
|
||||
lang = user.profile.telegram_lang_code
|
||||
if lang == 'es':
|
||||
text = f'¡Tu orden con ID {order.id} ha finalizado exitosamente!⚡ Unase a @robosats_es y ayudanos a mejorar.'
|
||||
else:
|
||||
text = f'Your order with ID {order.id} has finished successfully!⚡ Join us @robosats and help us improve.'
|
||||
|
||||
self.send_message(user, text)
|
||||
return
|
||||
|
||||
def public_order_cancelled(self, order):
|
||||
user = order.maker
|
||||
if not user.profile.telegram_enabled:
|
||||
return
|
||||
|
||||
lang = user.profile.telegram_lang_code
|
||||
if lang == 'es':
|
||||
text = f'Has cancelado tu orden pública con ID {order.id}.'
|
||||
else:
|
||||
text = f'You have cancelled your public order with ID {order.id}.'
|
||||
|
||||
self.send_message(user, text)
|
||||
return
|
||||
|
||||
def taker_canceled_b4bond(self, order):
|
||||
user = order.maker
|
||||
if not user.profile.telegram_enabled:
|
||||
return
|
||||
|
||||
lang = user.profile.telegram_lang_code
|
||||
if lang == 'es':
|
||||
text = f'El tomador ha cancelado antes de bloquear su fianza.'
|
||||
else:
|
||||
text = f'The taker has canceled before locking the bond.'
|
||||
|
||||
self.send_message(user, text)
|
||||
return
|
||||
|
||||
def taker_expired_b4bond(self, order):
|
||||
user = order.maker
|
||||
if not user.profile.telegram_enabled:
|
||||
return
|
||||
|
||||
lang = user.profile.telegram_lang_code
|
||||
if lang == 'es':
|
||||
text = f'El tomador no ha bloqueado la fianza a tiempo.'
|
||||
else:
|
||||
text = f'The taker has not locked the bond in time.'
|
||||
|
||||
self.send_message(user, text)
|
||||
return
|
||||
|
||||
def order_published(self, order):
|
||||
|
||||
time.sleep(1) # Just so this message always arrives after the previous two
|
||||
|
||||
user = order.maker
|
||||
lang = user.profile.telegram_lang_code
|
||||
|
||||
# In weird cases the order cannot be found (e.g. it is cancelled)
|
||||
|
||||
queryset = Order.objects.filter(maker=user)
|
||||
order = queryset.last()
|
||||
|
||||
print(str(order.id))
|
||||
if lang == 'es':
|
||||
text = f'Tu orden con ID {str(order.id)} es pública en el libro de ordenes.'
|
||||
else:
|
||||
text = f"Your order with ID {str(order.id)} is public in the order book."
|
||||
self.send_message(user, text)
|
||||
user.profile.telegram_welcomed = True
|
||||
user.profile.save()
|
||||
return
|
34
api/tasks.py
34
api/tasks.py
@ -152,3 +152,37 @@ def cache_market():
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
@shared_task(name="send_message", ignore_result=True)
|
||||
def send_message(order_id, message):
|
||||
|
||||
from api.models import Order
|
||||
order = Order.objects.get(id=order_id)
|
||||
if not order.maker.profile.telegram_enabled:
|
||||
return
|
||||
|
||||
from api.messages import Telegram
|
||||
telegram = Telegram()
|
||||
|
||||
if message == 'order_taken':
|
||||
telegram.order_taken(order)
|
||||
|
||||
elif message == 'order_expired_untaken':
|
||||
telegram.order_expired_untaken(order)
|
||||
|
||||
elif message == 'trade_successful':
|
||||
telegram.trade_successful(order)
|
||||
|
||||
elif message == 'public_order_cancelled':
|
||||
telegram.public_order_cancelled(order)
|
||||
|
||||
elif message == 'taker_expired_b4bond':
|
||||
telegram.taker_expired_b4bond(order)
|
||||
|
||||
elif message == 'taker_canceled_b4bond':
|
||||
telegram.taker_canceled_b4bond(order)
|
||||
|
||||
elif message == 'order_published':
|
||||
telegram.order_published(order)
|
||||
|
||||
return
|
12
api/views.py
12
api/views.py
@ -55,6 +55,16 @@ class MakerView(CreateAPIView):
|
||||
if not serializer.is_valid():
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# In case it gets overwhelming. Limit the number of public orders.
|
||||
if Order.objects.filter(status=Order.Status.PUB).count() >= int(config("MAX_PUBLIC_ORDERS")):
|
||||
return Response(
|
||||
{
|
||||
"bad_request":
|
||||
"Woah! RoboSats' book is at full capacity! Try again later"
|
||||
},
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
type = serializer.data.get("type")
|
||||
currency = serializer.data.get("currency")
|
||||
amount = serializer.data.get("amount")
|
||||
@ -311,7 +321,7 @@ class OrderView(viewsets.ViewSet):
|
||||
and order.maker_statement != "")
|
||||
elif data["is_taker"]:
|
||||
data["statement_submitted"] = (order.taker_statement != None
|
||||
and order.maker_statement != "")
|
||||
and order.taker_statement != "")
|
||||
|
||||
# 9) If status is 'Failed routing', reply with retry amounts, time of next retry and ask for invoice at third.
|
||||
elif (order.status == Order.Status.FAI
|
||||
|
@ -219,10 +219,10 @@ export default class MakerPage extends Component {
|
||||
</Grid>
|
||||
<br/>
|
||||
<Grid item xs={12} align="center">
|
||||
<Tooltip placement="top" enterTouchDelay="500" enterDelay="700" enterNextDelay="2000" title="Enter your prefered payment methods">
|
||||
<Tooltip placement="top" enterTouchDelay="300" enterDelay="700" enterNextDelay="2000" title="Enter your prefered fiat payment methods (instant recommended)">
|
||||
<TextField
|
||||
sx={{width:240}}
|
||||
label={this.state.currency==1000 ? "Swap Destination (e.g. rBTC)":"Instant Payment Method(s)"}
|
||||
label={this.state.currency==1000 ? "Swap Destination (e.g. rBTC)":"Fiat Payment Method(s)"}
|
||||
error={this.state.badPaymentMethod}
|
||||
helperText={this.state.badPaymentMethod ? "Must be shorter than 35 characters":""}
|
||||
type="text"
|
||||
|
@ -63,8 +63,8 @@ export default class OrderPage extends Component {
|
||||
"3": 2000, //'Waiting for taker bond'
|
||||
"4": 999999, //'Cancelled'
|
||||
"5": 999999, //'Expired'
|
||||
"6": 3000, //'Waiting for trade collateral and buyer invoice'
|
||||
"7": 3000, //'Waiting only for seller trade collateral'
|
||||
"6": 6000, //'Waiting for trade collateral and buyer invoice'
|
||||
"7": 8000, //'Waiting only for seller trade collateral'
|
||||
"8": 8000, //'Waiting only for buyer invoice'
|
||||
"9": 10000, //'Sending fiat - In chatroom'
|
||||
"10": 10000, //'Fiat sent - In chatroom'
|
||||
|
@ -195,6 +195,26 @@ export default class TradeBox extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
showBondIsSettled=()=>{
|
||||
return (
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography color="error" component="subtitle1" variant="subtitle1" align="center">
|
||||
⚖️ Your {this.props.data.is_maker ? 'maker' : 'taker'} bond was settled
|
||||
</Typography>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
showBondIsReturned=()=>{
|
||||
return (
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography color="green" component="subtitle1" variant="subtitle1" align="center">
|
||||
🔓 Your {this.props.data.is_maker ? 'maker' : 'taker'} bond was unlocked
|
||||
</Typography>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
showEscrowQRInvoice=()=>{
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
@ -476,10 +496,16 @@ export default class TradeBox extends Component {
|
||||
</Grid>
|
||||
<Grid item xs={12} align="left">
|
||||
<Typography component="body2" variant="body2">
|
||||
We are waiting for your trade counterparty statement.
|
||||
<p>We are waiting for your trade counterparty statement. If you are hesitant about
|
||||
the state of the dispute or want to add more information, contact robosats@protonmail.com.</p>
|
||||
|
||||
<p>Please, save the information needed to identificate your order and your payments: order ID;
|
||||
payment hashes of the bonds or escrow (check on your lightning wallet); exact amount of
|
||||
satoshis; and robot nickname. You will have to identify yourself as the user involved
|
||||
in this trade via email (or other contact methods).</p>
|
||||
</Typography>
|
||||
</Grid>
|
||||
{this.showBondIsLocked()}
|
||||
{this.showBondIsSettled()}
|
||||
</Grid>
|
||||
)
|
||||
}else{
|
||||
@ -496,7 +522,7 @@ export default class TradeBox extends Component {
|
||||
<Grid item xs={12} align="left">
|
||||
<Typography component="body2" variant="body2">
|
||||
Please, submit your statement. Be clear and specific about what happened and provide the necessary
|
||||
evidence. It is best to provide a burner email, XMPP or telegram username to follow up with the staff.
|
||||
evidence. You MUST provide a contact method: burner email, XMPP or telegram username to follow up with the staff.
|
||||
Disputes are solved at the discretion of real robots <i>(aka humans)</i>, so be as helpful
|
||||
as possible to ensure a fair outcome. Max 5000 chars.
|
||||
</Typography>
|
||||
@ -519,9 +545,8 @@ export default class TradeBox extends Component {
|
||||
<Grid item xs={12} align="center">
|
||||
<Button onClick={this.handleClickSubmitStatementButton} variant='contained' color='primary'>Submit</Button>
|
||||
</Grid>
|
||||
|
||||
{this.showBondIsLocked()}
|
||||
</Grid>
|
||||
{this.showBondIsSettled()}
|
||||
</Grid>
|
||||
)}
|
||||
}
|
||||
|
||||
@ -535,11 +560,60 @@ export default class TradeBox extends Component {
|
||||
</Grid>
|
||||
<Grid item xs={12} align="left">
|
||||
<Typography component="body2" variant="body2">
|
||||
Wait for the staff to resolve the dispute. The dispute winner
|
||||
will be asked to submit a LN invoice.
|
||||
<p>Both statements have been received, wait for the staff to resolve the dispute.
|
||||
The dispute winner will be asked to submit a LN invoice via the contact methods provided.
|
||||
If you are hesitant about the state of the dispute or want to add more information,
|
||||
contact robosats@protonmail.com. If you did not provide a contact method, write us inmediately. </p>
|
||||
|
||||
<p>Please, save the information needed to identificate your order and your payments: order ID;
|
||||
payment hashes of the bonds or escrow (check on your lightning wallet); exact amount of
|
||||
satoshis; and robot nickname. You will have to identify yourself as the user involved
|
||||
in this trade via email (or other contact methods).</p>
|
||||
</Typography>
|
||||
</Grid>
|
||||
{this.showBondIsLocked()}
|
||||
{this.showBondIsSettled()}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
showDisputeWinner=()=>{
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography color="primary" component="subtitle1" variant="subtitle1">
|
||||
<b> You have won the dispute </b>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="left">
|
||||
<Typography component="body2" variant="body2">
|
||||
You will be sent the satoshis of the escrow and your fidelity bond.
|
||||
This is not an automatic process, instead it will be sent manually by the staff.
|
||||
Please coordinate with the staff by writing to robosats@protonmail.com (or via your provided
|
||||
burner contact method). You will be asked to submit a new invoice together with identificative
|
||||
information about this order (bond payment hash, robot nicknames, exact amount in satoshis and order ID).
|
||||
</Typography>
|
||||
</Grid>
|
||||
{this.showBondIsSettled()}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
showDisputeLoser=()=>{
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography color="error" component="subtitle1" variant="subtitle1">
|
||||
<b> You have lost the dispute </b>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="left">
|
||||
<Typography component="body2" variant="body2">
|
||||
Unfortunately you have lost the dispute. If you think this is a mistake
|
||||
you can ask to re-open the case via email to robosats@protonmail.com. However,
|
||||
chances of it being investigated again are low.
|
||||
</Typography>
|
||||
</Grid>
|
||||
{this.showBondIsSettled()}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
@ -776,7 +850,8 @@ handleRatingRobosatsChange=(e)=>{
|
||||
<Grid item xs={12} align="center">
|
||||
<Button color='primary' onClick={() => {this.props.push('/')}}>Start Again</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{this.showBondIsReturned()}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
@ -851,7 +926,8 @@ handleRatingRobosatsChange=(e)=>{
|
||||
<Grid item xs={12} align="center">
|
||||
<Button onClick={this.handleClickSubmitInvoiceButton} variant='contained' color='primary'>Submit</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{this.showBondIsReturned()}
|
||||
</Grid>
|
||||
)
|
||||
}else{
|
||||
return(
|
||||
@ -874,7 +950,8 @@ handleRatingRobosatsChange=(e)=>{
|
||||
</ListItemText>
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{this.showBondIsReturned()}
|
||||
</Grid>
|
||||
)}
|
||||
}
|
||||
|
||||
@ -920,7 +997,9 @@ handleRatingRobosatsChange=(e)=>{
|
||||
{/* Trade Finished - TODO Needs more planning */}
|
||||
{this.props.data.status == 11 ? this.showInDisputeStatement() : ""}
|
||||
{this.props.data.status == 16 ? this.showWaitForDisputeResolution() : ""}
|
||||
|
||||
{(this.props.data.status == 17 & this.props.data.is_taker) || (this.props.data.status == 18 & this.props.data.is_maker) ? this.showDisputeWinner() : ""}
|
||||
{(this.props.data.status == 18 & this.props.data.is_taker) || (this.props.data.status == 17 & this.props.data.is_maker) ? this.showDisputeLoser() : ""}
|
||||
|
||||
{/* Order has expired */}
|
||||
{this.props.data.status == 5 ? this.showOrderExpired() : ""}
|
||||
{/* TODO */}
|
||||
|
@ -38,6 +38,31 @@ export default function getFlags(code){
|
||||
if(code == 'UYU') return '🇺🇾';
|
||||
if(code == 'PYG') return '🇵🇾';
|
||||
if(code == 'BOB') return '🇧🇴';
|
||||
if(code == 'IDR') return '🇮🇩';
|
||||
if(code == 'ANG') return '🇧🇶';
|
||||
if(code == 'CRC') return '🇨🇷';
|
||||
if(code == 'CUP') return '🇨🇺';
|
||||
if(code == 'DOP') return '🇩🇴';
|
||||
if(code == 'GHS') return '🇬🇭';
|
||||
if(code == 'GTQ') return '🇬🇹';
|
||||
if(code == 'ILS') return '🇮🇱';
|
||||
if(code == 'JMD') return '🇯🇲';
|
||||
if(code == 'KES') return '🇰🇪';
|
||||
if(code == 'KZT') return '🇰🇿';
|
||||
if(code == 'MYR') return '🇲🇲';
|
||||
if(code == 'NAD') return '🇳🇦';
|
||||
if(code == 'NGN') return '🇳🇬';
|
||||
if(code == 'AZN') return '🇦🇿';
|
||||
if(code == 'PAB') return '🇵🇦';
|
||||
if(code == 'PHP') return '🇵🇭';
|
||||
if(code == 'PKR') return '🇵🇰';
|
||||
if(code == 'QAR') return '🇶🇦';
|
||||
if(code == 'SAR') return '🇸🇦';
|
||||
if(code == 'THB') return '🇹🇭';
|
||||
if(code == 'TTD') return '🇹🇹';
|
||||
if(code == 'VND') return '🇻🇳';
|
||||
if(code == 'XOF') return '🇸🇳';
|
||||
if(code == 'XAU') return '🟨';
|
||||
if(code == 'BTC') return <SwapCallsIcon color="primary"/>;
|
||||
return '🏳';
|
||||
};
|
@ -35,5 +35,30 @@
|
||||
"34":"UYU",
|
||||
"35":"PYG",
|
||||
"36":"BOB",
|
||||
"37":"IDR",
|
||||
"38":"ANG",
|
||||
"39":"CRC",
|
||||
"40":"CUP",
|
||||
"41":"DOP",
|
||||
"42":"GHS",
|
||||
"43":"GTQ",
|
||||
"44":"ILS",
|
||||
"45":"JMD",
|
||||
"46":"KES",
|
||||
"47":"KZT",
|
||||
"48":"MYR",
|
||||
"49":"NAD",
|
||||
"50":"NGN",
|
||||
"51":"AZN",
|
||||
"52":"PAB",
|
||||
"53":"PHP",
|
||||
"54":"PKR",
|
||||
"55":"QAR",
|
||||
"56":"SAR",
|
||||
"57":"THB",
|
||||
"58":"TTD",
|
||||
"59":"VND",
|
||||
"60":"XOF",
|
||||
"300":"XAU",
|
||||
"1000":"BTC"
|
||||
}
|
252
frontend/static/css/fonts.css
Normal file
252
frontend/static/css/fonts.css
Normal file
@ -0,0 +1,252 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-1.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-2.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-3.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-4.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-5.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-6.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-7.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-8.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-9.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-10.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-11.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-12.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-13.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-14.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-15.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-16.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-17.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-18.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-19.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-20.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-21.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-22.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-23.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-24.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-25.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-26.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-27.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(/static/css/fonts/roboto-28.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
BIN
frontend/static/css/fonts/roboto-1.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-1.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-10.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-10.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-11.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-11.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-12.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-12.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-13.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-13.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-14.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-14.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-15.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-15.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-16.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-16.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-17.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-17.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-18.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-18.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-19.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-19.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-2.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-2.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-20.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-20.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-21.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-21.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-22.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-22.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-23.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-23.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-24.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-24.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-25.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-25.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-26.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-26.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-27.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-27.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-28.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-28.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-3.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-3.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-4.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-4.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-5.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-5.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-6.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-6.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-7.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-7.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-8.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-8.woff2
Normal file
Binary file not shown.
BIN
frontend/static/css/fonts/roboto-9.woff2
Normal file
BIN
frontend/static/css/fonts/roboto-9.woff2
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -11,13 +11,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>RoboSats - Simple and Private Bitcoin Exchange</title>
|
||||
{% load static %}
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "css/index.css" %}"
|
||||
/>
|
||||
<link rel="stylesheet" href="{% static "css/fonts.css" %}"/>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "css/index.css" %}"/>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
Loading…
Reference in New Issue
Block a user