ui: Add rate lookup helper when creating offers.

This commit is contained in:
tecnovert 2021-11-21 22:59:39 +02:00
parent 54ad0e496c
commit 8a9f4f9e38
No known key found for this signature in database
GPG Key ID: 8ED6D8750C4E3F93
11 changed files with 238 additions and 27 deletions

View File

@ -14,6 +14,7 @@ import base64
import random import random
import shutil import shutil
import struct import struct
import urllib.request
import hashlib import hashlib
import secrets import secrets
import datetime as dt import datetime as dt
@ -1036,6 +1037,9 @@ class BasicSwap(BaseApp):
msg_buf.amount_negotiable = extra_options.get('amount_negotiable', False) msg_buf.amount_negotiable = extra_options.get('amount_negotiable', False)
msg_buf.rate_negotiable = extra_options.get('rate_negotiable', False) msg_buf.rate_negotiable = extra_options.get('rate_negotiable', False)
if msg_buf.amount_negotiable or msg_buf.rate_negotiable:
ensure(auto_accept_bids is False, 'Auto-accept unavailable when amount or rate are variable')
if 'from_fee_override' in extra_options: if 'from_fee_override' in extra_options:
msg_buf.fee_rate_from = make_int(extra_options['from_fee_override'], self.ci(coin_from).exp()) msg_buf.fee_rate_from = make_int(extra_options['from_fee_override'], self.ci(coin_from).exp())
else: else:
@ -5519,3 +5523,26 @@ class BasicSwap(BaseApp):
if not self._network: if not self._network:
return {'Error': 'Not Initialised'} return {'Error': 'Not Initialised'}
return self._network.get_info() return self._network.get_info()
def lookupRates(self, coin_from, coin_to):
rv = {}
ci_from = self.ci(int(coin_from))
ci_to = self.ci(int(coin_to))
name_from = ci_from.coin_name().lower()
name_to = ci_to.coin_name().lower()
url = 'https://api.coingecko.com/api/v3/simple/price?ids={},{}&vs_currencies=usd'.format(name_from, name_to)
headers = {'User-Agent': 'Mozilla/5.0'}
req = urllib.request.Request(url, headers=headers)
js = json.loads(urllib.request.urlopen(req).read())
rate = float(js[name_from]['usd']) / float(js[name_to]['usd'])
js['rate'] = ci_to.format_amount(rate, conv_int=True, r=1)
rv['coingecko'] = js
url = 'https://api.bittrex.com/api/v1.1/public/getticker?market={}-{}'.format(ci_from.ticker(), ci_to.ticker())
headers = {'User-Agent': 'Mozilla/5.0'}
req = urllib.request.Request(url, headers=headers)
js = json.loads(urllib.request.urlopen(req).read())
rv['bittrex'] = js
return rv

View File

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019 tecnovert # Copyright (c) 2019-2021 tecnovert
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import urllib.request
import json import json
import urllib.request
class Explorer(): class Explorer():

View File

@ -42,6 +42,8 @@ from .js_server import (
js_network, js_network,
js_revokeoffer, js_revokeoffer,
js_smsgaddresses, js_smsgaddresses,
js_rates,
js_rate,
js_index, js_index,
) )
from .ui import ( from .ui import (
@ -507,6 +509,12 @@ class HttpHandler(BaseHTTPRequestHandler):
if 'amt_to' in parsed_data and 'amt_from' in parsed_data: if 'amt_to' in parsed_data and 'amt_from' in parsed_data:
parsed_data['rate'] = ci_from.make_int(parsed_data['amt_to'] / parsed_data['amt_from'], r=1) parsed_data['rate'] = ci_from.make_int(parsed_data['amt_to'] / parsed_data['amt_from'], r=1)
page_data['rate'] = ci_to.format_amount(parsed_data['rate'])
page_data['amt_var'] = True if have_data_entry(form_data, 'amt_var') else False
parsed_data['amt_var'] = page_data['amt_var']
page_data['rate_var'] = True if have_data_entry(form_data, 'rate_var') else False
parsed_data['rate_var'] = page_data['rate_var']
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:
@ -616,6 +624,11 @@ class HttpHandler(BaseHTTPRequestHandler):
if 'addr_to' in parsed_data: if 'addr_to' in parsed_data:
extra_options['addr_send_to'] = parsed_data['addr_to'] extra_options['addr_send_to'] = parsed_data['addr_to']
if parsed_data.get('amt_var', False):
extra_options['amount_negotiable'] = parsed_data['amt_var']
if parsed_data.get('rate_var', False):
extra_options['rate_negotiable'] = parsed_data['rate_var']
offer_id = swap_client.postOffer( offer_id = swap_client.postOffer(
parsed_data['coin_from'], parsed_data['coin_from'],
parsed_data['coin_to'], parsed_data['coin_to'],
@ -1129,6 +1142,8 @@ class HttpHandler(BaseHTTPRequestHandler):
'network': js_network, 'network': js_network,
'revokeoffer': js_revokeoffer, 'revokeoffer': js_revokeoffer,
'smsgaddresses': js_smsgaddresses, 'smsgaddresses': js_smsgaddresses,
'rate': js_rate,
'rates': js_rates,
}.get(url_split[2], js_index) }.get(url_split[2], js_index)
return func(self, url_split, post_string, is_json) return func(self, url_split, post_string, is_json)
except Exception as ex: except Exception as ex:

View File

@ -19,6 +19,7 @@ from .chainparams import (
) )
from .ui import ( from .ui import (
PAGE_LIMIT, PAGE_LIMIT,
getCoinType,
inputAmount, inputAmount,
describeBid, describeBid,
setCoinFilter, setCoinFilter,
@ -245,10 +246,6 @@ def js_revokeoffer(self, url_split, post_string, is_json):
return bytes(json.dumps({'revoked_offer': offer_id.hex()}), 'UTF-8') return bytes(json.dumps({'revoked_offer': offer_id.hex()}), 'UTF-8')
def js_index(self, url_split, post_string, is_json):
return bytes(json.dumps(self.server.swap_client.getSummary()), 'UTF-8')
def js_smsgaddresses(self, url_split, post_string, is_json): def js_smsgaddresses(self, url_split, post_string, is_json):
swap_client = self.server.swap_client swap_client = self.server.swap_client
if len(url_split) > 3: if len(url_split) > 3:
@ -276,3 +273,61 @@ def js_smsgaddresses(self, url_split, post_string, is_json):
return bytes(json.dumps({'edited_address': address}), 'UTF-8') return bytes(json.dumps({'edited_address': address}), 'UTF-8')
return bytes(json.dumps(swap_client.listAllSMSGAddresses()), 'UTF-8') return bytes(json.dumps(swap_client.listAllSMSGAddresses()), 'UTF-8')
def js_rates(self, url_split, post_string, is_json):
if post_string == '':
raise ValueError('No post data')
if is_json:
post_data = json.loads(post_string)
post_data['is_json'] = True
else:
post_data = urllib.parse.parse_qs(post_string)
sc = self.server.swap_client
coin_from = get_data_entry(post_data, 'coin_from')
coin_to = get_data_entry(post_data, 'coin_to')
return bytes(json.dumps(sc.lookupRates(coin_from, coin_to)), 'UTF-8')
def js_rate(self, url_split, post_string, is_json):
if post_string == '':
raise ValueError('No post data')
if is_json:
post_data = json.loads(post_string)
post_data['is_json'] = True
else:
post_data = urllib.parse.parse_qs(post_string)
sc = self.server.swap_client
coin_from = getCoinType(get_data_entry(post_data, 'coin_from'))
ci_from = sc.ci(coin_from)
coin_to = getCoinType(get_data_entry(post_data, 'coin_to'))
ci_to = sc.ci(coin_to)
# Set amount to if rate is provided
rate = get_data_entry_or(post_data, 'rate', None)
if rate is not None:
amt_from_str = get_data_entry_or(post_data, 'amt_from', None)
amt_to_str = get_data_entry_or(post_data, 'amt_to', None)
if amt_from_str is not None:
rate = ci_to.make_int(rate, r=1)
amt_from = inputAmount(amt_from_str, ci_from)
amount_to = ci_to.format_amount(int((amt_from * rate) // ci_from.COIN()), r=1)
return bytes(json.dumps({'amount_to': amount_to}), 'UTF-8')
if amt_to_str is not None:
rate = ci_from.make_int(1.0 / float(rate), r=1)
amt_to = inputAmount(amt_to_str, ci_to)
amount_from = ci_from.format_amount(int((amt_to * rate) // ci_to.COIN()), r=1)
return bytes(json.dumps({'amount_from': amount_from}), 'UTF-8')
amt_from = inputAmount(get_data_entry(post_data, 'amt_from'), ci_from)
amt_to = inputAmount(get_data_entry(post_data, 'amt_to'), ci_to)
rate = ci_to.format_amount(ci_from.make_int(amt_to / amt_from, r=1))
return bytes(json.dumps({'rate': rate}), 'UTF-8')
def js_index(self, url_split, post_string, is_json):
return bytes(json.dumps(self.server.swap_client.getSummary()), 'UTF-8')

View File

@ -60,8 +60,15 @@
<option value="-1" {% if data.nb_addr_from=="-1" %} selected{% endif %}>-- New Address --</option> <option value="-1" {% if data.nb_addr_from=="-1" %} selected{% endif %}>-- New Address --</option>
</select> </select>
</td></tr> </td></tr>
<tr><td>Minutes valid</td><td><input type="number" name="validmins" min="10" max="1440" value="{{ data.nb_validmins }}"></td></tr>
{% if data.amount_negotiable == true %}
<tr><td>Amount</td><td><input type="text" name="bid_amount" value="{{ data.amt_from }}"></td></tr>
{% endif %}
{% if data.rate_negotiable == true %}
<tr><td>Rate</td><td><input type="text" name="bid_rate" value="{{ data.rate }}"></td></tr>
{% endif %}
<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"></td></tr>
</table> </table>
{% else %} {% else %}

View File

@ -59,10 +59,13 @@
<option value="100"{% if data.fee_to_extra==100 %} selected{% endif %}>100%</option> <option value="100"{% if data.fee_to_extra==100 %} selected{% endif %}>100%</option>
</select></td></tr> </select></td></tr>
{% endif %} {% endif %}
</td><td>Rate</td><td><input type="text" id="rate" name="rate" value="{{ data.rate }}" readonly></td></tr>
<tr><td>Amount Variable</td><td colspan=3><input type="checkbox" id="amt_var" name="amt_var_" value="av" {% if data.amt_var==true %} checked="true"{% endif %} disabled></td></tr>
<tr><td>Rate Variable</td><td colspan=3><input type="checkbox" id="rate_var" name="rate_var_" value="rv" {% if data.rate_var==true %} checked="true"{% endif %} disabled></td></tr>
<tr class="padded_row"><td>Offer valid (hrs)</td><td><input type="number" name="validhrs" min="1" max="48" value="{{ data.validhrs }}" readonly></td></tr> <tr class="padded_row"><td>Offer valid (hrs)</td><td><input type="number" name="validhrs" min="1" max="48" value="{{ data.validhrs }}" readonly></td></tr>
<tr><td>Contract locked (hrs)</td><td><input type="number" name="lockhrs" min="1" max="64" value="{{ data.lockhrs }}" readonly></td>{% if data.swap_style != 'xmr' %}<td colspan=2>Participate txn will be locked for half the time.</td>{% endif %}</tr> <tr><td>Contract locked (hrs)</td><td><input type="number" name="lockhrs" min="1" max="64" value="{{ data.lockhrs }}" readonly></td>{% if data.swap_style != 'xmr' %}<td colspan=2>Participate txn will be locked for half the time.</td>{% endif %}</tr>
<tr><td>Auto Accept Bids</td><td colspan=3><input type="checkbox" name="autoaccept_" value="aa" {% if data.autoaccept==true %} checked="true"{% endif %} disabled></td></tr> <tr><td>Auto Accept Bids</td><td colspan=3><input type="checkbox" id="autoaccept" name="autoaccept_" value="aa" {% if data.autoaccept==true %} checked="true"{% endif %} disabled></td></tr>
</table> </table>
<input name="submit_offer" type="submit" value="Confirm Offer"> <input name="submit_offer" type="submit" value="Confirm Offer">
@ -77,6 +80,12 @@
{% if data.autoaccept==true %} {% if data.autoaccept==true %}
<input type="hidden" name="autoaccept" value="aa"> <input type="hidden" name="autoaccept" value="aa">
{% endif %} {% endif %}
{% if data.amt_var==true %}
<input type="hidden" name="amt_var" value="av">
{% endif %}
{% if data.rate_var==true %}
<input type="hidden" name="rate_var" value="rv">
{% endif %}
</form> </form>
</body></html> </body></html>

View File

@ -22,26 +22,112 @@
</select></td></tr> </select></td></tr>
<tr><td>Coin From</td><td> <tr><td>Coin From</td><td>
<select name="coin_from"><option value="-1">-- Select Coin --</option> <select id="coin_from" name="coin_from" onchange="set_rate('coin_from');"><option value="-1">-- Select Coin --</option>
{% for c in coins %} {% for c in coins %}
<option{% if data.coin_from==c[0] %} selected{% endif %} value="{{ c[0] }}">{{ c[1] }}</option> <option{% if data.coin_from==c[0] %} selected{% endif %} value="{{ c[0] }}">{{ c[1] }}</option>
{% endfor %} {% endfor %}
</select> </select>
</td><td>Amount From</td><td><input type="text" name="amt_from" value="{{ data.amt_from }}"></td><td>The amount you will send.</td></tr> </td><td>Amount From</td><td><input type="text" id="amt_from" name="amt_from" value="{{ data.amt_from }}" onchange="set_rate('amt_from');"></td><td>The amount you will send.</td></tr>
<tr><td>Coin To</td><td> <tr><td>Coin To</td><td>
<select name="coin_to"><option value="-1">-- Select Coin --</option> <select id="coin_to" name="coin_to" onchange="set_rate('coin_to');"><option value="-1">-- Select Coin --</option>
{% for c in coins %} {% for c in coins %}
<option{% if data.coin_to==c[0] %} selected{% endif %} value="{{ c[0] }}">{{ c[1] }}</option> <option{% if data.coin_to==c[0] %} selected{% endif %} value="{{ c[0] }}">{{ c[1] }}</option>
{% endfor %} {% endfor %}
</select> </select>
</td><td>Amount To</td><td><input type="text" name="amt_to" value="{{ data.amt_to }}"></td><td>The amount you will receive.</td></tr> </td><td>Amount To</td><td><input type="text" id="amt_to" name="amt_to" value="{{ data.amt_to }}" onchange="set_rate('amt_to');"></td><td>The amount you will receive.</td></tr>
</td><td>Rate</td><td><input type="text" id="rate" name="rate" value="{{ data.rate }}" onchange="set_rate('rate');"></td><td>Lock Rate: <input type="checkbox" id="rate_lock" name="rate_lock" value="rl"></td></tr>
<tr><td>Amount Variable</td><td><input type="checkbox" id="amt_var" name="amt_var" value="av" {% if data.amt_var==true %} checked="true"{% endif %}></td></tr>
<tr><td>Rate Variable</td><td><input type="checkbox" id="rate_var" name="rate_var" value="rv" {% if data.rate_var==true %} checked="true"{% endif %}></td></tr>
</table> </table>
<input name="continue" type="submit" value="Continue"> <input name="continue" type="submit" value="Continue">
<input name="check_rates" type="button" value="Lookup Rates" onclick='lookup_rates();'>
<input type="hidden" name="formid" value="{{ form_id }}"> <input type="hidden" name="formid" value="{{ form_id }}">
<input type="hidden" name="step1" value="a"> <input type="hidden" name="step1" value="a">
</form> </form>
<p id="rates_display"></p>
<p><a href="/">home</a></p> <p><a href="/">home</a></p>
<script>
const xhr_rates = new XMLHttpRequest();
xhr_rates.onload = () => {
if (xhr_rates.status == 200) {
const obj = JSON.parse(xhr_rates.response);
inner_html = '<pre><code>' + JSON.stringify(obj, null, ' ') + '</code></pre>';
document.getElementById('rates_display').innerHTML = inner_html;
}
}
const xhr_rate = new XMLHttpRequest();
xhr_rate.onload = () => {
if (xhr_rate.status == 200) {
const obj = JSON.parse(xhr_rate.response);
if (obj.hasOwnProperty('rate')) {
document.getElementById('rate').value = obj['rate'];
} else
if (obj.hasOwnProperty('amount_to')) {
document.getElementById('amt_to').value = obj['amount_to'];
} else
if (obj.hasOwnProperty('amount_from')) {
document.getElementById('amt_from').value = obj['amount_from'];
}
}
}
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;
}
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);
}
function set_rate(value_changed) {
const coin_from = document.getElementById('coin_from').value;
const coin_to = document.getElementById('coin_to').value;
const amt_from = document.getElementById('amt_from').value;
const amt_to = document.getElementById('amt_to').value;
const rate = document.getElementById('rate').value;
const lock_rate = document.getElementById('rate_lock').checked;
if (coin_from == '-1' || coin_to == '-1') {
return;
}
params = 'coin_from='+coin_from+'&coin_to='+coin_to;
if (value_changed == 'rate' || (lock_rate && value_changed == 'amt_from')) {
if (amt_from == '' || rate == '') {
return;
}
params += '&rate='+rate+'&amt_from='+amt_from;
} else
if (lock_rate && value_changed == 'amt_to') {
if (amt_to == '' || rate == '') {
return;
}
params += '&amt_to='+amt_to+'&rate='+rate;
} else {
if (amt_from == '' || amt_to == '') {
return;
}
params += '&amt_from='+amt_from+'&amt_to='+amt_to;
}
xhr_rate.open('POST', '/json/rate');
xhr_rate.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr_rate.send(params);
}
</script>
</body></html> </body></html>

View File

@ -56,10 +56,15 @@
<option value="100"{% if data.fee_to_extra==100 %} selected{% endif %}>100%</option> <option value="100"{% if data.fee_to_extra==100 %} selected{% endif %}>100%</option>
</select></td></tr> </select></td></tr>
{% endif %} {% endif %}
</td><td>Rate</td><td><input type="text" id="rate" name="rate" value="{{ data.rate }}" readonly></td></tr>
<tr><td>Amount Variable</td><td colspan=3><input type="checkbox" id="amt_var" name="amt_var_" value="av" {% if data.amt_var==true %} checked="true"{% endif %} disabled></td></tr>
<tr><td>Rate Variable</td><td colspan=3><input type="checkbox" id="rate_var" name="rate_var_" value="rv" {% if data.rate_var==true %} checked="true"{% endif %} disabled></td></tr>
<tr class="padded_row"><td>Offer valid (hrs)</td><td><input type="number" name="validhrs" min="1" max="48" value="{{ data.validhrs }}"></td></tr> <tr class="padded_row"><td>Offer valid (hrs)</td><td><input type="number" name="validhrs" min="1" max="48" value="{{ data.validhrs }}"></td></tr>
<tr><td>Contract locked (hrs)</td><td><input type="number" name="lockhrs" min="1" max="96" value="{{ data.lockhrs }}"></td>{% if data.swap_style != 'xmr' %}<td colspan=2>Participate txn will be locked for half the time.</td>{% endif %}</tr> <tr><td>Contract locked (hrs)</td><td><input type="number" name="lockhrs" min="1" max="96" value="{{ data.lockhrs }}"></td>{% if data.swap_style != 'xmr' %}<td colspan=2>Participate txn will be locked for half the time.</td>{% endif %}</tr>
<tr><td>Auto Accept Bids</td><td colspan=3><input type="checkbox" name="autoaccept" value="aa" {% if data.autoaccept==true %} checked="true"{% endif %}></td></tr> <tr><td>Auto Accept Bids</td><td colspan=3><input type="checkbox" id="autoaccept" name="autoaccept" value="aa" {% if data.autoaccept==true %} checked="true"{% endif %}></td></tr>
</table> </table>
<input name="check_offer" type="submit" value="Continue"> <input name="check_offer" type="submit" value="Continue">
@ -69,6 +74,12 @@
<input type="hidden" name="addr_from" value="{{ data.addr_from }}"> <input type="hidden" name="addr_from" value="{{ data.addr_from }}">
<input type="hidden" name="coin_from" value="{{ data.coin_from }}"> <input type="hidden" name="coin_from" value="{{ data.coin_from }}">
<input type="hidden" name="coin_to" value="{{ data.coin_to }}"> <input type="hidden" name="coin_to" value="{{ data.coin_to }}">
{% if data.amt_var==true %}
<input type="hidden" name="amt_var" value="true">
{% endif %}
{% if data.rate_var==true %}
<input type="hidden" name="rate_var" value="true">
{% endif %}
</form> </form>
</body></html> </body></html>

View File

@ -313,7 +313,7 @@ def prepareCore(coin, version_pair, settings, data_dir):
extractCore(coin, version_pair, settings, bin_dir, release_path) extractCore(coin, version_pair, settings, bin_dir, release_path)
def prepareDataDir(coin, settings, chain, particl_mnemonic, use_containers=False)): def prepareDataDir(coin, settings, chain, particl_mnemonic, use_containers=False):
core_settings = settings['chainclients'][coin] core_settings = settings['chainclients'][coin]
bin_dir = core_settings['bindir'] bin_dir = core_settings['bindir']
data_dir = core_settings['datadir'] data_dir = core_settings['datadir']

View File

@ -2,21 +2,22 @@
0.0.27 0.0.27
============== ==============
- Track failed and successful swaps by address - Track failed and successful swaps by address.
- Added rate lookup helper when creating offer.
0.0.26 0.0.26
============== ==============
- Added protocol version to order and bid messages - Added protocol version to order and bid messages.
- Moved chain start heights to bid. - Moved chain start heights to bid.
- Avoid scantxoutset for decred style swaps - Avoid scantxoutset for decred style swaps.
- xmr: spend chain B lock tx will look for existing spends - xmr: spend chain B lock tx will look for existing spends.
- xmrswaps: - xmrswaps:
- Setting state to 'Script tx redeemed' will trigger an attempt to redeem the scriptless lock tx. - Setting state to 'Script tx redeemed' will trigger an attempt to redeem the scriptless lock tx.
- Node will wait for the chain B lock tx to reach a spendable depth before attempting to spend. - Node will wait for the chain B lock tx to reach a spendable depth before attempting to spend.
- ui: Sort settings page by coin name. - ui: Sort settings page by coin name.
- ui, xmr: List of candidate remote XMR daemon urls can be set through the http ui - ui, xmr: List of candidate remote XMR daemon urls can be set through the http ui.
0.0.25 0.0.25
@ -24,8 +25,8 @@
- Fix extra 33 bytes in lock spend fee calculation. - Fix extra 33 bytes in lock spend fee calculation.
- XMR swaps use watchonly addresses to save the lock tx to the wallet - XMR swaps use watchonly addresses to save the lock tx to the wallet
- Instead of scantxoutset - Instead of scantxoutset.
- Add missing check of leader's lock refund tx signature result - Add missing check of leader's lock refund tx signature result.
- Blind part -> XMR swaps are possible: - Blind part -> XMR swaps are possible:
- The sha256 hash of the chain b view private key is used as the nonce for transactions requiring cooperation to sign. - The sha256 hash of the chain b view private key is used as the nonce for transactions requiring cooperation to sign.
- Follower sends a public key in xmr_swap.dest_af. - Follower sends a public key in xmr_swap.dest_af.
@ -45,14 +46,14 @@
0.0.23 0.0.23
============== ==============
- Enables private offers - Enables private offers.
0.0.22 0.0.22
============== ==============
- Improved wallets page - Improved wallets page
- Consistent wallet order - Consistent wallet order.
- Separated RPC calls into threads. - Separated RPC calls into threads.
@ -67,5 +68,5 @@
0.0.6 0.0.6
============== ==============
- Experimental support for XMR swaps. - Experimental support for XMR swaps
- Single direction only, scriptless -> XMR - Single direction only, scriptless -> XMR.

View File

@ -29,10 +29,10 @@ from basicswap.basicswap_util import (
SEQUENCE_LOCK_BLOCKS, SEQUENCE_LOCK_BLOCKS,
SEQUENCE_LOCK_TIME) SEQUENCE_LOCK_TIME)
from basicswap.util import ( from basicswap.util import (
SerialiseNum,
DeserialiseNum,
make_int, make_int,
SerialiseNum,
format_amount, format_amount,
DeserialiseNum,
validate_amount) validate_amount)