mirror of
https://github.com/basicswap/basicswap.git
synced 2025-01-05 18:29:26 +00:00
Variable bid amount and rate.
This commit is contained in:
parent
8a9f4f9e38
commit
99534756de
8 changed files with 215 additions and 52 deletions
|
@ -984,6 +984,14 @@ class BasicSwap(BaseApp):
|
||||||
if valid_for_seconds > 24 * 60 * 60:
|
if valid_for_seconds > 24 * 60 * 60:
|
||||||
raise ValueError('Bid TTL too high')
|
raise ValueError('Bid TTL too high')
|
||||||
|
|
||||||
|
def validateBidAmount(self, offer, bid_amount, bid_rate):
|
||||||
|
ensure(bid_amount >= offer.min_bid_amount, 'Bid amount below minimum')
|
||||||
|
ensure(bid_amount <= offer.amount_from, 'Bid amount above offer amount')
|
||||||
|
if not offer.amount_negotiable:
|
||||||
|
ensure(offer.amount_from == bid_amount, 'Bid amount must match offer amount.')
|
||||||
|
if not offer.rate_negotiable:
|
||||||
|
ensure(offer.rate == bid_rate, 'Bid rate must match offer rate.')
|
||||||
|
|
||||||
def getOfferAddressTo(self, extra_options):
|
def getOfferAddressTo(self, extra_options):
|
||||||
if 'addr_send_to' in extra_options:
|
if 'addr_send_to' in extra_options:
|
||||||
return extra_options['addr_send_to']
|
return extra_options['addr_send_to']
|
||||||
|
@ -1613,7 +1621,7 @@ class BasicSwap(BaseApp):
|
||||||
return q[0]
|
return q[0]
|
||||||
|
|
||||||
def postBid(self, offer_id, amount, addr_send_from=None, extra_options={}):
|
def postBid(self, offer_id, amount, addr_send_from=None, extra_options={}):
|
||||||
# Bid to send bid.amount * offer.rate of coin_to in exchange for bid.amount of coin_from
|
# Bid to send bid.amount * bid.rate of coin_to in exchange for bid.amount of coin_from
|
||||||
self.log.debug('postBid %s', offer_id.hex())
|
self.log.debug('postBid %s', offer_id.hex())
|
||||||
|
|
||||||
offer = self.getOffer(offer_id)
|
offer = self.getOffer(offer_id)
|
||||||
|
@ -1627,10 +1635,7 @@ class BasicSwap(BaseApp):
|
||||||
self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, valid_for_seconds)
|
self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, valid_for_seconds)
|
||||||
|
|
||||||
bid_rate = extra_options.get('bid_rate', offer.rate)
|
bid_rate = extra_options.get('bid_rate', offer.rate)
|
||||||
if not offer.amount_negotiable:
|
self.validateBidAmount(offer, amount, bid_rate)
|
||||||
ensure(offer.amount_from == int(amount), 'Bid amount must match offer amount.')
|
|
||||||
if not offer.rate_negotiable:
|
|
||||||
ensure(offer.rate == bid_rate, 'Bid rate must match offer rate.')
|
|
||||||
|
|
||||||
self.mxDB.acquire()
|
self.mxDB.acquire()
|
||||||
try:
|
try:
|
||||||
|
@ -1650,7 +1655,7 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
contract_count = self.getNewContractId()
|
contract_count = self.getNewContractId()
|
||||||
|
|
||||||
amount_to = int((msg_buf.amount * offer.rate) // self.ci(coin_from).COIN())
|
amount_to = int((msg_buf.amount * bid_rate) // self.ci(coin_from).COIN())
|
||||||
|
|
||||||
now = int(time.time())
|
now = int(time.time())
|
||||||
if offer.swap_type == SwapTypes.SELLER_FIRST:
|
if offer.swap_type == SwapTypes.SELLER_FIRST:
|
||||||
|
@ -1958,7 +1963,7 @@ class BasicSwap(BaseApp):
|
||||||
self.swaps_in_progress[bid_id] = (bid, offer)
|
self.swaps_in_progress[bid_id] = (bid, offer)
|
||||||
|
|
||||||
def postXmrBid(self, offer_id, amount, addr_send_from=None, extra_options={}):
|
def postXmrBid(self, offer_id, amount, addr_send_from=None, extra_options={}):
|
||||||
# Bid to send bid.amount * offer.rate of coin_to in exchange for bid.amount of coin_from
|
# Bid to send bid.amount * bid.rate of coin_to in exchange for bid.amount of coin_from
|
||||||
# Send MSG1L F -> L
|
# Send MSG1L F -> L
|
||||||
self.log.debug('postXmrBid %s', offer_id.hex())
|
self.log.debug('postXmrBid %s', offer_id.hex())
|
||||||
|
|
||||||
|
@ -1979,10 +1984,7 @@ class BasicSwap(BaseApp):
|
||||||
ci_to = self.ci(coin_to)
|
ci_to = self.ci(coin_to)
|
||||||
|
|
||||||
bid_rate = extra_options.get('bid_rate', offer.rate)
|
bid_rate = extra_options.get('bid_rate', offer.rate)
|
||||||
if not offer.amount_negotiable:
|
self.validateBidAmount(offer, amount, bid_rate)
|
||||||
ensure(offer.amount_from == int(amount), 'Bid amount must match offer amount.')
|
|
||||||
if not offer.rate_negotiable:
|
|
||||||
ensure(offer.rate == bid_rate, 'Bid rate must match offer rate.')
|
|
||||||
|
|
||||||
self.checkSynced(coin_from, coin_to)
|
self.checkSynced(coin_from, coin_to)
|
||||||
|
|
||||||
|
@ -2080,7 +2082,7 @@ class BasicSwap(BaseApp):
|
||||||
rate=msg_buf.rate,
|
rate=msg_buf.rate,
|
||||||
created_at=bid_created_at,
|
created_at=bid_created_at,
|
||||||
contract_count=xmr_swap.contract_count,
|
contract_count=xmr_swap.contract_count,
|
||||||
amount_to=(msg_buf.amount * offer.rate) // ci_from.COIN(),
|
amount_to=(msg_buf.amount * msg_buf.rate) // ci_from.COIN(),
|
||||||
expire_at=bid_created_at + msg_buf.time_valid,
|
expire_at=bid_created_at + msg_buf.time_valid,
|
||||||
bid_addr=bid_addr,
|
bid_addr=bid_addr,
|
||||||
was_sent=True,
|
was_sent=True,
|
||||||
|
@ -2397,7 +2399,7 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
amount_to = bid.amount_to
|
amount_to = bid.amount_to
|
||||||
# Check required?
|
# Check required?
|
||||||
assert(amount_to == (bid.amount * offer.rate) // self.ci(offer.coin_from).COIN())
|
assert(amount_to == (bid.amount * bid.rate) // self.ci(offer.coin_from).COIN())
|
||||||
|
|
||||||
if bid.debug_ind == DebugTypes.MAKE_INVALID_PTX:
|
if bid.debug_ind == DebugTypes.MAKE_INVALID_PTX:
|
||||||
amount_to -= 1
|
amount_to -= 1
|
||||||
|
@ -3773,15 +3775,9 @@ class BasicSwap(BaseApp):
|
||||||
ensure(offer.state == OfferStates.OFFER_RECEIVED, 'Bad offer state')
|
ensure(offer.state == OfferStates.OFFER_RECEIVED, 'Bad offer state')
|
||||||
ensure(msg['to'] == offer.addr_from, 'Received on incorrect address')
|
ensure(msg['to'] == offer.addr_from, 'Received on incorrect address')
|
||||||
ensure(now <= offer.expire_at, 'Offer expired')
|
ensure(now <= offer.expire_at, 'Offer expired')
|
||||||
ensure(bid_data.amount >= offer.min_bid_amount, 'Bid amount below minimum')
|
|
||||||
ensure(bid_data.amount <= offer.amount_from, 'Bid amount above offer amount')
|
|
||||||
self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, bid_data.time_valid)
|
self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, bid_data.time_valid)
|
||||||
ensure(now <= msg['sent'] + bid_data.time_valid, 'Bid expired')
|
ensure(now <= msg['sent'] + bid_data.time_valid, 'Bid expired')
|
||||||
|
self.validateBidAmount(offer, bid_data.amount, bid_data.rate)
|
||||||
if not offer.amount_negotiable:
|
|
||||||
ensure(offer.amount_from == bid_data.amount, 'Bid amount must match offer amount.')
|
|
||||||
if not offer.rate_negotiable:
|
|
||||||
ensure(offer.rate == bid_data.rate, 'Bid rate must match offer rate.')
|
|
||||||
|
|
||||||
# TODO: Allow higher bids
|
# TODO: Allow higher bids
|
||||||
# assert(bid_data.rate != offer['data'].rate), 'Bid rate mismatch'
|
# assert(bid_data.rate != offer['data'].rate), 'Bid rate mismatch'
|
||||||
|
@ -3790,7 +3786,7 @@ class BasicSwap(BaseApp):
|
||||||
ci_from = self.ci(offer.coin_from)
|
ci_from = self.ci(offer.coin_from)
|
||||||
ci_to = self.ci(coin_to)
|
ci_to = self.ci(coin_to)
|
||||||
|
|
||||||
amount_to = int((bid_data.amount * offer.rate) // ci_from.COIN())
|
amount_to = int((bid_data.amount * bid_data.rate) // ci_from.COIN())
|
||||||
swap_type = offer.swap_type
|
swap_type = offer.swap_type
|
||||||
if swap_type == SwapTypes.SELLER_FIRST:
|
if swap_type == SwapTypes.SELLER_FIRST:
|
||||||
ensure(len(bid_data.pkhash_buyer) == 20, 'Bad pkhash_buyer length')
|
ensure(len(bid_data.pkhash_buyer) == 20, 'Bad pkhash_buyer length')
|
||||||
|
@ -4067,15 +4063,10 @@ class BasicSwap(BaseApp):
|
||||||
raise ValueError('Bad offer state')
|
raise ValueError('Bad offer state')
|
||||||
ensure(msg['to'] == offer.addr_from, 'Received on incorrect address')
|
ensure(msg['to'] == offer.addr_from, 'Received on incorrect address')
|
||||||
ensure(now <= offer.expire_at, 'Offer expired')
|
ensure(now <= offer.expire_at, 'Offer expired')
|
||||||
ensure(bid_data.amount >= offer.min_bid_amount, 'Bid amount below minimum')
|
|
||||||
ensure(bid_data.amount <= offer.amount_from, 'Bid amount above offer amount')
|
|
||||||
self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, bid_data.time_valid)
|
self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, bid_data.time_valid)
|
||||||
ensure(now <= msg['sent'] + bid_data.time_valid, 'Bid expired')
|
ensure(now <= msg['sent'] + bid_data.time_valid, 'Bid expired')
|
||||||
|
|
||||||
if not offer.amount_negotiable:
|
self.validateBidAmount(offer, bid_data.amount, bid_data.rate)
|
||||||
ensure(offer.amount_from == bid_data.amount, 'Bid amount must match offer amount.')
|
|
||||||
if not offer.rate_negotiable:
|
|
||||||
ensure(offer.rate == bid_data.rate, 'Bid rate must match offer rate.')
|
|
||||||
|
|
||||||
ensure(ci_to.verifyKey(bid_data.kbvf), 'Invalid chain B follower view key')
|
ensure(ci_to.verifyKey(bid_data.kbvf), 'Invalid chain B follower view key')
|
||||||
ensure(ci_from.verifyPubkey(bid_data.pkaf), 'Invalid chain A follower public key')
|
ensure(ci_from.verifyPubkey(bid_data.pkaf), 'Invalid chain A follower public key')
|
||||||
|
@ -4092,7 +4083,7 @@ class BasicSwap(BaseApp):
|
||||||
amount=bid_data.amount,
|
amount=bid_data.amount,
|
||||||
rate=bid_data.rate,
|
rate=bid_data.rate,
|
||||||
created_at=msg['sent'],
|
created_at=msg['sent'],
|
||||||
amount_to=(bid_data.amount * offer.rate) // ci_from.COIN(),
|
amount_to=(bid_data.amount * bid_data.rate) // ci_from.COIN(),
|
||||||
expire_at=msg['sent'] + bid_data.time_valid,
|
expire_at=msg['sent'] + bid_data.time_valid,
|
||||||
bid_addr=msg['from'],
|
bid_addr=msg['from'],
|
||||||
was_received=True,
|
was_received=True,
|
||||||
|
@ -5525,24 +5516,75 @@ class BasicSwap(BaseApp):
|
||||||
return self._network.get_info()
|
return self._network.get_info()
|
||||||
|
|
||||||
def lookupRates(self, coin_from, coin_to):
|
def lookupRates(self, coin_from, coin_to):
|
||||||
|
self.log.debug('lookupRates {}, {}'.format(coin_from, coin_to))
|
||||||
rv = {}
|
rv = {}
|
||||||
ci_from = self.ci(int(coin_from))
|
ci_from = self.ci(int(coin_from))
|
||||||
ci_to = self.ci(int(coin_to))
|
ci_to = self.ci(int(coin_to))
|
||||||
|
|
||||||
name_from = ci_from.coin_name().lower()
|
headers = {'Connection': 'close'}
|
||||||
name_to = ci_to.coin_name().lower()
|
name_from = ci_from.chainparams()['name']
|
||||||
|
name_to = ci_to.chainparams()['name']
|
||||||
url = 'https://api.coingecko.com/api/v3/simple/price?ids={},{}&vs_currencies=usd'.format(name_from, name_to)
|
url = 'https://api.coingecko.com/api/v3/simple/price?ids={},{}&vs_currencies=usd'.format(name_from, name_to)
|
||||||
headers = {'User-Agent': 'Mozilla/5.0'}
|
start = time.time()
|
||||||
req = urllib.request.Request(url, headers=headers)
|
req = urllib.request.Request(url, headers=headers)
|
||||||
js = json.loads(urllib.request.urlopen(req).read())
|
js = json.loads(urllib.request.urlopen(req, timeout=10).read())
|
||||||
|
js['time_taken'] = time.time() - start
|
||||||
rate = float(js[name_from]['usd']) / float(js[name_to]['usd'])
|
rate = float(js[name_from]['usd']) / float(js[name_to]['usd'])
|
||||||
js['rate'] = ci_to.format_amount(rate, conv_int=True, r=1)
|
js['rate_inferred'] = ci_to.format_amount(rate, conv_int=True, r=1)
|
||||||
rv['coingecko'] = js
|
rv['coingecko'] = js
|
||||||
|
|
||||||
url = 'https://api.bittrex.com/api/v1.1/public/getticker?market={}-{}'.format(ci_from.ticker(), ci_to.ticker())
|
ticker_from = ci_from.chainparams()['ticker']
|
||||||
headers = {'User-Agent': 'Mozilla/5.0'}
|
ticker_to = ci_to.chainparams()['ticker']
|
||||||
|
if ci_from.coin_type() == Coins.BTC:
|
||||||
|
pair = '{}-{}'.format(ticker_from, ticker_to)
|
||||||
|
url = 'https://api.bittrex.com/api/v1.1/public/getticker?market=' + pair
|
||||||
|
start = time.time()
|
||||||
req = urllib.request.Request(url, headers=headers)
|
req = urllib.request.Request(url, headers=headers)
|
||||||
js = json.loads(urllib.request.urlopen(req).read())
|
js = json.loads(urllib.request.urlopen(req, timeout=10).read())
|
||||||
|
js['time_taken'] = time.time() - start
|
||||||
|
js['pair'] = pair
|
||||||
|
|
||||||
|
try:
|
||||||
|
rate_inverted = ci_from.make_int(1.0 / float(js['result']['Last']), r=1)
|
||||||
|
js['rate_inferred'] = ci_to.format_amount(rate_inverted)
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning('lookupRates error: %s', str(e))
|
||||||
|
js['rate_inferred'] = 'error'
|
||||||
|
|
||||||
rv['bittrex'] = js
|
rv['bittrex'] = js
|
||||||
|
elif ci_to.coin_type() == Coins.BTC:
|
||||||
|
pair = '{}-{}'.format(ticker_to, ticker_from)
|
||||||
|
url = 'https://api.bittrex.com/api/v1.1/public/getticker?market=' + pair
|
||||||
|
start = time.time()
|
||||||
|
req = urllib.request.Request(url, headers=headers)
|
||||||
|
js = json.loads(urllib.request.urlopen(req, timeout=10).read())
|
||||||
|
js['time_taken'] = time.time() - start
|
||||||
|
js['pair'] = pair
|
||||||
|
js['rate_last'] = js['result']['Last']
|
||||||
|
rv['bittrex'] = js
|
||||||
|
else:
|
||||||
|
pair = 'BTC-{}'.format(ticker_from)
|
||||||
|
url = 'https://api.bittrex.com/api/v1.1/public/getticker?market=' + pair
|
||||||
|
start = time.time()
|
||||||
|
req = urllib.request.Request(url, headers=headers)
|
||||||
|
js_from = json.loads(urllib.request.urlopen(req, timeout=10).read())
|
||||||
|
js_from['time_taken'] = time.time() - start
|
||||||
|
js_from['pair'] = pair
|
||||||
|
|
||||||
|
pair = 'BTC-{}'.format(ticker_to)
|
||||||
|
url = 'https://api.bittrex.com/api/v1.1/public/getticker?market=' + pair
|
||||||
|
start = time.time()
|
||||||
|
req = urllib.request.Request(url, headers=headers)
|
||||||
|
js_to = json.loads(urllib.request.urlopen(req, timeout=10).read())
|
||||||
|
js_to['time_taken'] = time.time() - start
|
||||||
|
js_to['pair'] = pair
|
||||||
|
|
||||||
|
try:
|
||||||
|
rate_inferred = float(js_from['result']['Last']) / float(js_to['result']['Last'])
|
||||||
|
rate_inferred = ci_to.format_amount(rate, conv_int=True, r=1)
|
||||||
|
except Exception as e:
|
||||||
|
rate_inferred = 'error'
|
||||||
|
|
||||||
|
rv['bittrex'] = {'from': js_from, 'to': js_to, 'rate_inferred': rate_inferred}
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
|
|
|
@ -497,7 +497,9 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
try:
|
try:
|
||||||
page_data['amt_from'] = get_data_entry(form_data, 'amt_from')
|
page_data['amt_from'] = get_data_entry(form_data, 'amt_from')
|
||||||
parsed_data['amt_from'] = inputAmount(page_data['amt_from'], ci_from)
|
parsed_data['amt_from'] = inputAmount(page_data['amt_from'], ci_from)
|
||||||
parsed_data['min_bid'] = int(parsed_data['amt_from'])
|
|
||||||
|
# TODO: Add min_bid to the ui
|
||||||
|
parsed_data['min_bid'] = ci_from.chainparams_network()['min_amount']
|
||||||
except Exception:
|
except Exception:
|
||||||
errors.append('Amount From')
|
errors.append('Amount From')
|
||||||
|
|
||||||
|
@ -516,6 +518,10 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
page_data['rate_var'] = True if have_data_entry(form_data, 'rate_var') else False
|
page_data['rate_var'] = True if have_data_entry(form_data, 'rate_var') else False
|
||||||
parsed_data['rate_var'] = page_data['rate_var']
|
parsed_data['rate_var'] = page_data['rate_var']
|
||||||
|
|
||||||
|
# Change default autoaccept to false
|
||||||
|
if page_data['amt_var'] or page_data['rate_var']:
|
||||||
|
page_data['autoaccept'] = False
|
||||||
|
|
||||||
if b'step1' in form_data:
|
if b'step1' in form_data:
|
||||||
if len(errors) == 0 and b'continue' in form_data:
|
if len(errors) == 0 and b'continue' in form_data:
|
||||||
page_data['step2'] = True
|
page_data['step2'] = True
|
||||||
|
@ -717,6 +723,14 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
sent_bid_id = None
|
sent_bid_id = None
|
||||||
show_bid_form = None
|
show_bid_form = None
|
||||||
form_data = self.checkForm(post_string, 'offer', messages)
|
form_data = self.checkForm(post_string, 'offer', messages)
|
||||||
|
|
||||||
|
ci_from = swap_client.ci(Coins(offer.coin_from))
|
||||||
|
ci_to = swap_client.ci(Coins(offer.coin_to))
|
||||||
|
|
||||||
|
# Set defaults
|
||||||
|
bid_amount = ci_from.format_amount(offer.amount_from)
|
||||||
|
bid_rate = ci_to.format_amount(offer.rate)
|
||||||
|
|
||||||
if form_data:
|
if form_data:
|
||||||
if b'revoke_offer' in form_data:
|
if b'revoke_offer' in form_data:
|
||||||
try:
|
try:
|
||||||
|
@ -739,20 +753,29 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
extra_options = {
|
extra_options = {
|
||||||
'valid_for_seconds': minutes_valid * 60,
|
'valid_for_seconds': minutes_valid * 60,
|
||||||
}
|
}
|
||||||
sent_bid_id = swap_client.postBid(offer_id, offer.amount_from, addr_send_from=addr_from, extra_options=extra_options).hex()
|
if have_data_entry(form_data, 'bid_rate'):
|
||||||
|
bid_rate = get_data_entry(form_data, 'bid_rate')
|
||||||
|
extra_options['bid_rate'] = ci_to.make_int(bid_rate, r=1)
|
||||||
|
|
||||||
|
if have_data_entry(form_data, 'bid_amount'):
|
||||||
|
bid_amount = get_data_entry(form_data, 'bid_amount')
|
||||||
|
amount_from = inputAmount(bid_amount, ci_from)
|
||||||
|
else:
|
||||||
|
amount_from = offer.amount_from
|
||||||
|
|
||||||
|
sent_bid_id = swap_client.postBid(offer_id, amount_from, addr_send_from=addr_from, extra_options=extra_options).hex()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
messages.append('Error: Send bid failed: ' + str(ex))
|
messages.append('Error: Send bid failed: ' + str(ex))
|
||||||
show_bid_form = True
|
show_bid_form = True
|
||||||
|
|
||||||
ci_from = swap_client.ci(Coins(offer.coin_from))
|
|
||||||
ci_to = swap_client.ci(Coins(offer.coin_to))
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'tla_from': ci_from.ticker(),
|
'tla_from': ci_from.ticker(),
|
||||||
'tla_to': ci_to.ticker(),
|
'tla_to': ci_to.ticker(),
|
||||||
'state': strOfferState(offer.state),
|
'state': strOfferState(offer.state),
|
||||||
'coin_from': ci_from.coin_name(),
|
'coin_from': ci_from.coin_name(),
|
||||||
'coin_to': ci_to.coin_name(),
|
'coin_to': ci_to.coin_name(),
|
||||||
|
'coin_from_ind': int(ci_from.coin_type()),
|
||||||
|
'coin_to_ind': int(ci_to.coin_type()),
|
||||||
'amt_from': ci_from.format_amount(offer.amount_from),
|
'amt_from': ci_from.format_amount(offer.amount_from),
|
||||||
'amt_to': ci_to.format_amount((offer.amount_from * offer.rate) // ci_from.COIN()),
|
'amt_to': ci_to.format_amount((offer.amount_from * offer.rate) // ci_from.COIN()),
|
||||||
'rate': ci_to.format_amount(offer.rate),
|
'rate': ci_to.format_amount(offer.rate),
|
||||||
|
@ -767,6 +790,8 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
'show_bid_form': show_bid_form,
|
'show_bid_form': show_bid_form,
|
||||||
'amount_negotiable': offer.amount_negotiable,
|
'amount_negotiable': offer.amount_negotiable,
|
||||||
'rate_negotiable': offer.rate_negotiable,
|
'rate_negotiable': offer.rate_negotiable,
|
||||||
|
'bid_amount': bid_amount,
|
||||||
|
'bid_rate': bid_rate,
|
||||||
}
|
}
|
||||||
data.update(extend_data)
|
data.update(extend_data)
|
||||||
|
|
||||||
|
@ -1097,6 +1122,13 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
def page_shutdown(self, url_split, post_string):
|
def page_shutdown(self, url_split, post_string):
|
||||||
swap_client = self.server.swap_client
|
swap_client = self.server.swap_client
|
||||||
|
|
||||||
|
if len(url_split) > 2:
|
||||||
|
token = url_split[2]
|
||||||
|
expect_token = self.server.session_tokens.get('shutdown', None)
|
||||||
|
if token != expect_token:
|
||||||
|
return self.page_info('Unexpected token, still running.')
|
||||||
|
|
||||||
swap_client.stopRunning()
|
swap_client.stopRunning()
|
||||||
|
|
||||||
return self.page_info('Shutting down')
|
return self.page_info('Shutting down')
|
||||||
|
@ -1105,13 +1137,17 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
swap_client = self.server.swap_client
|
swap_client = self.server.swap_client
|
||||||
summary = swap_client.getSummary()
|
summary = swap_client.getSummary()
|
||||||
|
|
||||||
|
shutdown_token = os.urandom(8).hex()
|
||||||
|
self.server.session_tokens['shutdown'] = shutdown_token
|
||||||
|
|
||||||
template = env.get_template('index.html')
|
template = env.get_template('index.html')
|
||||||
return bytes(template.render(
|
return bytes(template.render(
|
||||||
title=self.server.title,
|
title=self.server.title,
|
||||||
refresh=30,
|
refresh=30,
|
||||||
h2=self.server.title,
|
h2=self.server.title,
|
||||||
version=__version__,
|
version=__version__,
|
||||||
summary=summary
|
summary=summary,
|
||||||
|
shutdown_token=shutdown_token
|
||||||
), 'UTF-8')
|
), 'UTF-8')
|
||||||
|
|
||||||
def page_404(self, url_split):
|
def page_404(self, url_split):
|
||||||
|
@ -1249,6 +1285,7 @@ class HttpThread(threading.Thread, HTTPServer):
|
||||||
self.swap_client = swap_client
|
self.swap_client = swap_client
|
||||||
self.title = 'BasicSwap, ' + self.swap_client.chain
|
self.title = 'BasicSwap, ' + self.swap_client.chain
|
||||||
self.last_form_id = dict()
|
self.last_form_id = dict()
|
||||||
|
self.session_tokens = dict()
|
||||||
|
|
||||||
self.timeout = 60
|
self.timeout = 60
|
||||||
HTTPServer.__init__(self, (self.host_name, self.port_no), HttpHandler)
|
HTTPServer.__init__(self, (self.host_name, self.port_no), HttpHandler)
|
||||||
|
|
|
@ -23,7 +23,7 @@ Version: {{ version }}
|
||||||
|
|
||||||
<p><a href="/newoffer">New Offer</a></p>
|
<p><a href="/newoffer">New Offer</a></p>
|
||||||
|
|
||||||
<p><a href="/shutdown">Shutdown</a></p>
|
<p><a href="/shutdown/{{ shutdown_token }}">Shutdown</a></p>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% if data.show_bid_form %}
|
{% if data.show_bid_form %}
|
||||||
<br/><h4>New Bid</h4>
|
<br/><h4>New Bid</h4>
|
||||||
<p>You will send {{ data.amt_to }} {{ data.tla_to }} and receive {{ data.amt_from }} {{ data.tla_from }}
|
<p>You will send <span id="bid_amt_to">{{ data.amt_to }}</span> {{ data.tla_to }} and receive <span id="bid_amt_from">{{ data.amt_from }}</span> {{ data.tla_from }}
|
||||||
{% if data.xmr_type == true %}
|
{% if data.xmr_type == true %}
|
||||||
(excluding {{ data.amt_from_lock_spend_tx_fee }} {{ data.tla_from }} in tx fees).
|
(excluding {{ data.amt_from_lock_spend_tx_fee }} {{ data.tla_from }} in tx fees).
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -62,24 +62,37 @@
|
||||||
</td></tr>
|
</td></tr>
|
||||||
|
|
||||||
{% if data.amount_negotiable == true %}
|
{% if data.amount_negotiable == true %}
|
||||||
<tr><td>Amount</td><td><input type="text" name="bid_amount" value="{{ data.amt_from }}"></td></tr>
|
<tr><td>Amount</td><td><input type="text" id="bid_amount" name="bid_amount" value="{{ data.bid_amount }}" onchange="updateBidParams('amount');"></td></tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if data.rate_negotiable == true %}
|
{% if data.rate_negotiable == true %}
|
||||||
<tr><td>Rate</td><td><input type="text" name="bid_rate" value="{{ data.rate }}"></td></tr>
|
<tr><td>Rate</td><td><input type="text" id="bid_rate" name="bid_rate" value="{{ data.bid_rate }}" onchange="updateBidParams('rate');"></td></tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<tr><td>Minutes valid</td><td><input type="number" name="validmins" min="10" max="1440" value="{{ data.nb_validmins }}"></td></tr>
|
<tr><td>Minutes valid</td><td><input type="number" name="validmins" min="10" max="1440" value="{{ data.nb_validmins }}"></td></tr>
|
||||||
<tr><td><input type="submit" name="sendbid" value="Send Bid"><input type="submit" name="cancel" value="Cancel"></td></tr>
|
<tr><td>
|
||||||
|
<input type="submit" name="sendbid" value="Send Bid">
|
||||||
|
<input type="submit" name="cancel" value="Cancel">
|
||||||
|
<input name="check_rates" type="button" value="Lookup Rates" onclick='lookup_rates();'>
|
||||||
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
<input type="submit" name="newbid" value="New Bid">
|
<input type="submit" name="newbid" value="New Bid">
|
||||||
{% if data.sent == 'True' and data.was_revoked != true %}
|
{% if data.sent == 'True' and data.was_revoked != true %}
|
||||||
<input name="revoke_offer" type="submit" value="Revoke Offer" onclick="return confirmPopup();">
|
<input name="revoke_offer" type="submit" value="Revoke Offer" onclick="return confirmPopup();">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<input name="check_rates" type="button" value="Lookup Rates" onclick='lookup_rates();'>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<input type="hidden" id="coin_from" value="{{ data.coin_from_ind }}">
|
||||||
|
<input type="hidden" id="coin_to" value="{{ data.coin_to_ind }}">
|
||||||
|
<input type="hidden" id="amt_var" value="{{ data.amount_negotiable }}">
|
||||||
|
<input type="hidden" id="rate_var" value="{{ data.rate_negotiable }}">
|
||||||
|
<input type="hidden" id="amount_from" value="{{ data.amt_from }}">
|
||||||
|
<input type="hidden" id="offer_rate" value="{{ data.rate }}">
|
||||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<p id="rates_display"></p>
|
||||||
|
|
||||||
<h4>Bids</h4>
|
<h4>Bids</h4>
|
||||||
<table>
|
<table>
|
||||||
<tr><th>Bid ID</th><th>Bid Amount</th><th>Bid Rate</th><th>Bid Status</th><th>Identity From</th></tr>
|
<tr><th>Bid ID</th><th>Bid Amount</th><th>Bid Rate</th><th>Bid Status</th><th>Identity From</th></tr>
|
||||||
|
@ -91,8 +104,72 @@
|
||||||
<p><a href="/">home</a></p>
|
<p><a href="/">home</a></p>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
const xhr_rates = new XMLHttpRequest();
|
||||||
|
xhr_rates.onload = () => {
|
||||||
|
if (xhr_rates.status == 200) {
|
||||||
|
const obj = JSON.parse(xhr_rates.response);
|
||||||
|
|
||||||
|
inner_html = '<h4>Rates</h4><pre><code>' + JSON.stringify(obj, null, ' ') + '</code></pre>';
|
||||||
|
document.getElementById('rates_display').innerHTML = inner_html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookup_rates() {
|
||||||
|
const coin_from = document.getElementById('coin_from').value;
|
||||||
|
const coin_to = document.getElementById('coin_to').value;
|
||||||
|
|
||||||
|
if (coin_from == '-1' || coin_to == '-1') {
|
||||||
|
alert('Coins from and to must be set first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_html = '<h4>Rates</h4><p>Updating...</p>';
|
||||||
|
document.getElementById('rates_display').innerHTML = inner_html;
|
||||||
|
|
||||||
|
xhr_rates.open('POST', '/json/rates');
|
||||||
|
xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
|
xhr_rates.send('coin_from='+coin_from+'&coin_to='+coin_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
const xhr_bid_params = new XMLHttpRequest();
|
||||||
|
xhr_bid_params.onload = () => {
|
||||||
|
if (xhr_bid_params.status == 200) {
|
||||||
|
const obj = JSON.parse(xhr_bid_params.response);
|
||||||
|
document.getElementById('bid_amt_to').innerHTML = obj['amount_to'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBidParams(value_changed) {
|
||||||
|
const coin_from = document.getElementById('coin_from').value;
|
||||||
|
const coin_to = document.getElementById('coin_to').value;
|
||||||
|
const amt_var = document.getElementById('amt_var').value;
|
||||||
|
const rate_var = document.getElementById('rate_var').value;
|
||||||
|
|
||||||
|
let amt_from = '';
|
||||||
|
let rate = '';
|
||||||
|
if (amt_var) {
|
||||||
|
amt_from = document.getElementById('bid_amount').value;
|
||||||
|
} else {
|
||||||
|
amt_from = document.getElementById('amount_from').value;
|
||||||
|
}
|
||||||
|
if (rate_var) {
|
||||||
|
rate = document.getElementById('bid_rate').value;
|
||||||
|
} else {
|
||||||
|
rate = document.getElementById('offer_rate').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value_changed == 'amount') {
|
||||||
|
document.getElementById('bid_amt_from').innerHTML = amt_from;
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr_bid_params.open('POST', '/json/rate');
|
||||||
|
xhr_bid_params.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
|
xhr_bid_params.send('coin_from='+coin_from+'&coin_to='+coin_to+'&rate='+rate+'&amt_from='+amt_from);
|
||||||
|
}
|
||||||
|
|
||||||
function confirmPopup() {
|
function confirmPopup() {
|
||||||
return confirm("Are you sure?");
|
return confirm("Are you sure?");
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body></html>
|
</body></html>
|
||||||
|
|
|
@ -57,7 +57,7 @@ xhr_rates.onload = () => {
|
||||||
if (xhr_rates.status == 200) {
|
if (xhr_rates.status == 200) {
|
||||||
const obj = JSON.parse(xhr_rates.response);
|
const obj = JSON.parse(xhr_rates.response);
|
||||||
|
|
||||||
inner_html = '<pre><code>' + JSON.stringify(obj, null, ' ') + '</code></pre>';
|
inner_html = '<h4>Rates</h4><pre><code>' + JSON.stringify(obj, null, ' ') + '</code></pre>';
|
||||||
document.getElementById('rates_display').innerHTML = inner_html;
|
document.getElementById('rates_display').innerHTML = inner_html;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,9 @@ function lookup_rates() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner_html = '<h4>Rates</h4><p>Updating...</p>';
|
||||||
|
document.getElementById('rates_display').innerHTML = inner_html;
|
||||||
|
|
||||||
xhr_rates.open('POST', '/json/rates');
|
xhr_rates.open('POST', '/json/rates');
|
||||||
xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
xhr_rates.send('coin_from='+coin_from+'&coin_to='+coin_to);
|
xhr_rates.send('coin_from='+coin_from+'&coin_to='+coin_to);
|
||||||
|
@ -106,7 +109,7 @@ function set_rate(value_changed) {
|
||||||
}
|
}
|
||||||
|
|
||||||
params = 'coin_from='+coin_from+'&coin_to='+coin_to;
|
params = 'coin_from='+coin_from+'&coin_to='+coin_to;
|
||||||
if (value_changed == 'rate' || (lock_rate && value_changed == 'amt_from')) {
|
if (value_changed == 'rate' || (lock_rate && value_changed == 'amt_from') || (amt_to == '' && value_changed == 'amt_from')) {
|
||||||
if (amt_from == '' || rate == '') {
|
if (amt_from == '' || rate == '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,7 +206,7 @@ def describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, bid_events, edit_b
|
||||||
'coin_from': ci_from.coin_name(),
|
'coin_from': ci_from.coin_name(),
|
||||||
'coin_to': ci_to.coin_name(),
|
'coin_to': ci_to.coin_name(),
|
||||||
'amt_from': ci_from.format_amount(bid.amount),
|
'amt_from': ci_from.format_amount(bid.amount),
|
||||||
'amt_to': ci_to.format_amount((bid.amount * offer.rate) // ci_from.COIN()),
|
'amt_to': ci_to.format_amount((bid.amount * bid.rate) // ci_from.COIN()),
|
||||||
'bid_rate': ci_to.format_amount(bid.rate),
|
'bid_rate': ci_to.format_amount(bid.rate),
|
||||||
'ticker_from': ticker_from,
|
'ticker_from': ticker_from,
|
||||||
'ticker_to': ticker_to,
|
'ticker_to': ticker_to,
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
- Track failed and successful swaps by address.
|
- Track failed and successful swaps by address.
|
||||||
- Added rate lookup helper when creating offer.
|
- Added rate lookup helper when creating offer.
|
||||||
|
- Prevent old shutdown link from shutting down a new session.
|
||||||
|
|
||||||
|
|
||||||
0.0.26
|
0.0.26
|
||||||
|
|
|
@ -497,6 +497,9 @@ class BaseTest(unittest.TestCase):
|
||||||
class Test(BaseTest):
|
class Test(BaseTest):
|
||||||
__test__ = True
|
__test__ = True
|
||||||
|
|
||||||
|
def notest_00_delay(self):
|
||||||
|
test_delay_event.wait(100000)
|
||||||
|
|
||||||
def test_01_part_xmr(self):
|
def test_01_part_xmr(self):
|
||||||
logging.info('---------- Test PART to XMR')
|
logging.info('---------- Test PART to XMR')
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
|
|
Loading…
Reference in a new issue