diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 2722f46..a88c41b 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -5870,92 +5870,131 @@ class BasicSwap(BaseApp): return {'Error': 'Not Initialised'} return self._network.get_info() - def lookupRates(self, coin_from, coin_to): + def lookupRates(self, coin_from, coin_to, output_array=False): self.log.debug('lookupRates {}, {}'.format(coin_from, coin_to)) - bittrex_api_v3 = 'https://api.bittrex.com/v3' + rate_sources = self.settings.get('rate_sources', {}) + ci_from = self.ci(int(coin_from)) + ci_to = self.ci(int(coin_to)) + name_from = ci_from.chainparams()['name'] + name_to = ci_to.chainparams()['name'] + ticker_from = ci_from.chainparams()['ticker'] + ticker_to = ci_to.chainparams()['ticker'] + headers = {'Connection': 'close'} try: self.setConnectionParameters() rv = {} - try: - ci_from = self.ci(int(coin_from)) - ci_to = self.ci(int(coin_to)) - headers = {'Connection': 'close'} - 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) - self.log.debug(f'lookupRates: {url}') - 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 - rate = float(js[name_from]['usd']) / float(js[name_to]['usd']) - js['rate_inferred'] = ci_to.format_amount(rate, conv_int=True, r=1) - rv['coingecko'] = js - except Exception as e: - rv['coingecko_error'] = str(e) - - try: - ticker_from = ci_from.chainparams()['ticker'] - ticker_to = ci_to.chainparams()['ticker'] - if ci_from.coin_type() == Coins.BTC: - pair = f'{ticker_to}-{ticker_from}' - url = f'{bittrex_api_v3}/markets/{pair}/ticker' + if rate_sources.get('coingecko.com', True): + try: + url = 'https://api.coingecko.com/api/v3/simple/price?ids={},{}&vs_currencies=usd,btc'.format(name_from, name_to) self.log.debug(f'lookupRates: {url}') 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 + rate = float(js[name_from]['usd']) / float(js[name_to]['usd']) + js['rate_inferred'] = ci_to.format_amount(rate, conv_int=True, r=1) + rv['coingecko'] = js + except Exception as e: + rv['coingecko_error'] = str(e) - try: - rate_inverted = ci_from.make_int(1.0 / float(js['lastTradeRate']), 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' + if rate_sources.get('bittrex.com', True): + bittrex_api_v3 = 'https://api.bittrex.com/v3' + try: + if ci_from.coin_type() == Coins.BTC: + pair = f'{ticker_to}-{ticker_from}' + url = f'{bittrex_api_v3}/markets/{pair}/ticker' + self.log.debug(f'lookupRates: {url}') + 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 + try: + rate_inverted = ci_from.make_int(1.0 / float(js['lastTradeRate']), 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' + js['from_btc'] = 1.0 + js['to_btc'] = js['lastTradeRate'] + rv['bittrex'] = js + elif ci_to.coin_type() == Coins.BTC: + pair = f'{ticker_from}-{ticker_to}' + url = f'{bittrex_api_v3}/markets/{pair}/ticker' + self.log.debug(f'lookupRates: {url}') + 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['lastTradeRate'] + js['from_btc'] = js['lastTradeRate'] + js['to_btc'] = 1.0 + rv['bittrex'] = js + else: + pair = f'{ticker_from}-BTC' + url = f'{bittrex_api_v3}/markets/{pair}/ticker' + self.log.debug(f'lookupRates: {url}') + 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 - rv['bittrex'] = js - elif ci_to.coin_type() == Coins.BTC: - pair = f'{ticker_from}-{ticker_to}' - url = f'{bittrex_api_v3}/markets/{pair}/ticker' - self.log.debug(f'lookupRates: {url}') - 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['lastTradeRate'] - rv['bittrex'] = js - else: - pair = f'{ticker_from}-BTC' - url = f'{bittrex_api_v3}/markets/{pair}/ticker' - self.log.debug(f'lookupRates: {url}') - 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 = f'{ticker_to}-BTC' + url = f'{bittrex_api_v3}/markets/{pair}/ticker' + self.log.debug(f'lookupRates: {url}') + 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 - pair = f'{ticker_to}-BTC' - url = f'{bittrex_api_v3}/markets/{pair}/ticker' - self.log.debug(f'lookupRates: {url}') - 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['lastTradeRate']) / float(js_to['lastTradeRate']) + rate_inferred = ci_to.format_amount(rate, conv_int=True, r=1) + except Exception as e: + rate_inferred = 'error' - try: - rate_inferred = float(js_from['lastTradeRate']) / float(js_to['lastTradeRate']) - 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, + 'from_btc': js_from['lastTradeRate'], + 'to_btc': js_to['lastTradeRate'] + } + except Exception as e: + rv['bittrex_error'] = str(e) - rv['bittrex'] = {'from': js_from, 'to': js_to, 'rate_inferred': rate_inferred} - except Exception as e: - rv['bittrex_error'] = str(e) + if output_array: + + def format_float(f): + return '{:.12f}'.format(f).rstrip('0').rstrip('.') + + rv_array = [] + if 'coingecko_error' in rv: + rv_array.append(('coingecko.com', 'error', rv['coingecko_error'])) + if 'coingecko' in rv: + js = rv['coingecko'] + rv_array.append(( + 'coingecko.com', + ticker_from, + ticker_to, + format_float(float(js[name_from]['usd'])), + format_float(float(js[name_to]['usd'])), + format_float(float(js[name_from]['btc'])), + format_float(float(js[name_to]['btc'])), + format_float(float(js['rate_inferred'])), + )) + if 'bittrex_error' in rv: + rv_array.append(('bittrex.com', 'error', rv['bittrex_error'])) + if 'bittrex' in rv: + js = rv['bittrex'] + rate = js['rate_last'] if 'rate_last' in js else js['rate_inferred'] + rv_array.append(('bittrex.com', ticker_from, ticker_to, '', '', js['from_btc'], js['to_btc'], format_float(float(rate)))) + return rv_array return rv finally: diff --git a/basicswap/http_server.py b/basicswap/http_server.py index 7e3f5b6..8a99e71 100644 --- a/basicswap/http_server.py +++ b/basicswap/http_server.py @@ -43,6 +43,7 @@ from .js_server import ( js_network, js_revokeoffer, js_smsgaddresses, + js_rates_list, js_rates, js_rate, js_index, @@ -654,24 +655,29 @@ class HttpHandler(BaseHTTPRequestHandler): self.end_headers() def handle_http(self, status_code, path, post_string='', is_json=False): - url_split = self.path.split('/') + parsed = urllib.parse.urlparse(self.path) + url_split = parsed.path.split('/') + if post_string == '' and len(parsed.query) > 0: + post_string = parsed.query if len(url_split) > 1 and url_split[1] == 'json': try: self.putHeaders(status_code, 'text/plain') func = js_index if len(url_split) > 2: - func = {'coins': js_coins, - 'wallets': js_wallets, - 'offers': js_offers, - 'sentoffers': js_sentoffers, - 'bids': js_bids, - 'sentbids': js_sentbids, - 'network': js_network, - 'revokeoffer': js_revokeoffer, - 'smsgaddresses': js_smsgaddresses, - 'rate': js_rate, - 'rates': js_rates, - }.get(url_split[2], js_index) + func = { + 'coins': js_coins, + 'wallets': js_wallets, + 'offers': js_offers, + 'sentoffers': js_sentoffers, + 'bids': js_bids, + 'sentbids': js_sentbids, + 'network': js_network, + 'revokeoffer': js_revokeoffer, + 'smsgaddresses': js_smsgaddresses, + 'rate': js_rate, + 'rates': js_rates, + 'rateslist': js_rates_list, + }.get(url_split[2], js_index) return func(self, url_split, post_string, is_json) except Exception as ex: if self.server.swap_client.debug is True: diff --git a/basicswap/js_server.py b/basicswap/js_server.py index cd14f20..73e1ada 100644 --- a/basicswap/js_server.py +++ b/basicswap/js_server.py @@ -17,6 +17,7 @@ from .basicswap_util import ( from .chainparams import ( Coins, chainparams, + getCoinIdFromTicker, ) from .ui.util import ( PAGE_LIMIT, @@ -339,6 +340,15 @@ def js_rates(self, url_split, post_string, is_json): return bytes(json.dumps(sc.lookupRates(coin_from, coin_to)), 'UTF-8') +def js_rates_list(self, url_split, query_string, is_json): + get_data = urllib.parse.parse_qs(query_string) + + sc = self.server.swap_client + coin_from = getCoinIdFromTicker(get_data['from'][0]) + coin_to = getCoinIdFromTicker(get_data['to'][0]) + return bytes(json.dumps(sc.lookupRates(coin_from, coin_to, True)), 'UTF-8') + + def js_rate(self, url_split, post_string, is_json): if post_string == '': raise ValueError('No post data') diff --git a/tests/basicswap/common.py b/tests/basicswap/common.py index 4b1934a..5dea5dc 100644 --- a/tests/basicswap/common.py +++ b/tests/basicswap/common.py @@ -213,7 +213,7 @@ def read_json_api(port, path=None): url = f'http://127.0.0.1:{port}/json' if path is not None: url += '/' + path - return json.loads(urlopen(url, timeout=30).read()) + return json.loads(urlopen(url, timeout=300).read()) def post_json_api(port, path, json_data): diff --git a/tests/basicswap/test_run.py b/tests/basicswap/test_run.py index 4700836..9148821 100644 --- a/tests/basicswap/test_run.py +++ b/tests/basicswap/test_run.py @@ -110,9 +110,8 @@ class Test(BaseTest): assert ('coingecko' in rv) assert ('bittrex' in rv) - rv = self.swap_clients[0].lookupRates(Coins.LTC, Coins.PART) - assert ('coingecko' in rv) - assert ('bittrex' in rv) + rv = read_json_api(1800, 'rateslist?from=PART&to=BTC') + assert(len(rv) == 2) def test_01_verifyrawtransaction(self): txn = '0200000001eb6e5c4ebba4efa32f40c7314cad456a64008e91ee30b2dd0235ab9bb67fbdbb01000000ee47304402200956933242dde94f6cf8f195a470f8d02aef21ec5c9b66c5d3871594bdb74c9d02201d7e1b440de8f4da672d689f9e37e98815fb63dbc1706353290887eb6e8f7235012103dc1b24feb32841bc2f4375da91fa97834e5983668c2a39a6b7eadb60e7033f9d205a803b28fe2f86c17db91fa99d7ed2598f79b5677ffe869de2e478c0d1c02cc7514c606382012088a8201fe90717abb84b481c2a59112414ae56ec8acc72273642ca26cc7a5812fdc8f68876a914225fbfa4cb725b75e511810ac4d6f74069bdded26703520140b27576a914207eb66b2fd6ed9924d6217efc7fa7b38dfabe666888acffffffff01e0167118020000001976a9140044e188928710cecba8311f1cf412135b98145c88ac00000000'