diff --git a/lbry/extras/daemon/exchange_rate_manager.py b/lbry/extras/daemon/exchange_rate_manager.py index ccc3c77bb..16108981c 100644 --- a/lbry/extras/daemon/exchange_rate_manager.py +++ b/lbry/extras/daemon/exchange_rate_manager.py @@ -58,9 +58,12 @@ class MarketFeed: raise NotImplementedError() async def get_response(self): - async with aiohttp_request('get', self.url, params=self.params, timeout=self.request_timeout) as response: + headers = {"User-Agent": "lbrynet"} + async with aiohttp_request( + 'get', self.url, params=self.params, timeout=self.request_timeout, headers=headers + ) as response: try: - self._last_response = await response.json() + self._last_response = await response.json(content_type=None) except ContentTypeError as e: self._last_response = {} log.warning("Could not parse exchange rate response from %s: %s", self.name, e.message) @@ -104,10 +107,10 @@ class MarketFeed: self.event.clear() -class BittrexFeed(MarketFeed): +class BaseBittrexFeed(MarketFeed): name = "Bittrex" - market = "BTCLBC" - url = "https://api.bittrex.com/v3/markets/LBC-BTC/ticker" + market = None + url = None fee = 0.0025 def get_rate_from_response(self, json_response): @@ -116,49 +119,103 @@ class BittrexFeed(MarketFeed): return 1.0 / float(json_response['lastTradeRate']) -class LBRYFeed(MarketFeed): - name = "lbry.com" +class BittrexBTCFeed(BaseBittrexFeed): market = "BTCLBC" - url = "https://api.lbry.com/lbc/exchange_rate" + url = "https://api.bittrex.com/v3/markets/LBC-BTC/ticker" + + +class BittrexUSDFeed(BaseBittrexFeed): + market = "USDLBC" + url = "https://api.bittrex.com/v3/markets/LBC-USD/ticker" + + +class BaseCryptonatorFeed(MarketFeed): + name = "Cryptonator" + market = None + url = None def get_rate_from_response(self, json_response): - if 'data' not in json_response: - raise InvalidExchangeRateResponseError(self.name, 'result not found') - return 1.0 / json_response['data']['lbc_btc'] - - -class LBRYBTCFeed(LBRYFeed): - market = "USDBTC" - - def get_rate_from_response(self, json_response): - if 'data' not in json_response: - raise InvalidExchangeRateResponseError(self.name, 'result not found') - return 1.0 / json_response['data']['btc_usd'] - - -class CryptonatorFeed(MarketFeed): - name = "cryptonator.com" - market = "BTCLBC" - url = "https://api.cryptonator.com/api/ticker/btc-lbc" - - def get_rate_from_response(self, json_response): - if 'ticker' not in json_response or len(json_response['ticker']) == 0 or \ - 'success' not in json_response or json_response['success'] is not True: + if 'ticker' not in json_response or 'price' not in json_response['ticker']: raise InvalidExchangeRateResponseError(self.name, 'result not found') return float(json_response['ticker']['price']) -class CryptonatorBTCFeed(CryptonatorFeed): - market = "USDBTC" - url = "https://api.cryptonator.com/api/ticker/usd-btc" +class CryptonatorBTCFeed(BaseCryptonatorFeed): + market = "BTCLBC" + url = "https://api.cryptonator.com/api/ticker/btc-lbc" + + +class CryptonatorUSDFeed(BaseCryptonatorFeed): + market = "USDLBC" + url = "https://api.cryptonator.com/api/ticker/usd-lbc" + + +class BaseCoinExFeed(MarketFeed): + name = "CoinEx" + market = None + url = None + + def get_rate_from_response(self, json_response): + if 'data' not in json_response or \ + 'ticker' not in json_response['data'] or \ + 'last' not in json_response['data']['ticker']: + raise InvalidExchangeRateResponseError(self.name, 'result not found') + return 1.0 / float(json_response['data']['ticker']['last']) + + +class CoinExBTCFeed(BaseCoinExFeed): + market = "BTCLBC" + url = "https://api.coinex.com/v1/market/ticker?market=LBCBTC" + + +class CoinExUSDFeed(BaseCoinExFeed): + market = "USDLBC" + url = "https://api.coinex.com/v1/market/ticker?market=LBCUSDT" + + +class BaseHotbitFeed(MarketFeed): + name = "hotbit" + market = None + url = "https://api.hotbit.io/api/v1/market.last" + + def get_rate_from_response(self, json_response): + if 'result' not in json_response: + raise InvalidExchangeRateResponseError(self.name, 'result not found') + return 1.0 / float(json_response['result']) + + +class HotbitBTCFeed(BaseHotbitFeed): + market = "BTCLBC" + params = {"market": "LBC/BTC"} + + +class HotbitUSDFeed(BaseHotbitFeed): + market = "USDLBC" + params = {"market": "LBC/USDT"} + + +class UPbitBTCFeed(MarketFeed): + name = "UPbit" + market = "BTCLBC" + url = "https://api.upbit.com/v1/ticker" + params = {"markets": "BTC-LBC"} + + def get_rate_from_response(self, json_response): + if len(json_response) != 1 or 'trade_price' not in json_response[0]: + raise InvalidExchangeRateResponseError(self.name, 'result not found') + return 1.0 / float(json_response[0]['trade_price']) FEEDS: Iterable[Type[MarketFeed]] = ( - LBRYFeed, - LBRYBTCFeed, - BittrexFeed, - # CryptonatorFeed, - # CryptonatorBTCFeed, + BittrexBTCFeed, + BittrexUSDFeed, + CryptonatorBTCFeed, + CryptonatorUSDFeed, + CoinExBTCFeed, + CoinExUSDFeed, + HotbitBTCFeed, + HotbitUSDFeed, + UPbitBTCFeed, ) diff --git a/lbry/testcase.py b/lbry/testcase.py index 0c72279b5..bf880b703 100644 --- a/lbry/testcase.py +++ b/lbry/testcase.py @@ -29,7 +29,7 @@ from lbry.extras.daemon.components import ( ) from lbry.extras.daemon.componentmanager import ComponentManager from lbry.extras.daemon.exchange_rate_manager import ( - ExchangeRateManager, ExchangeRate, LBRYFeed, LBRYBTCFeed + ExchangeRateManager, ExchangeRate, BittrexBTCFeed, BittrexUSDFeed ) from lbry.extras.daemon.storage import SQLiteStorage from lbry.blob.blob_manager import BlobManager @@ -282,8 +282,8 @@ class FakeExchangeRateManager(ExchangeRateManager): def get_fake_exchange_rate_manager(rates=None): return FakeExchangeRateManager( - [LBRYFeed(), LBRYBTCFeed()], - rates or {'BTCLBC': 3.0, 'USDBTC': 2.0} + [BittrexBTCFeed(), BittrexUSDFeed()], + rates or {'BTCLBC': 3.0, 'USDLBC': 2.0} ) diff --git a/lbry/utils.py b/lbry/utils.py index 0b5a4c826..317e1dc53 100644 --- a/lbry/utils.py +++ b/lbry/utils.py @@ -375,7 +375,7 @@ def get_ssl_context() -> ssl.SSLContext: @contextlib.asynccontextmanager async def aiohttp_request(method, url, **kwargs) -> typing.AsyncContextManager[aiohttp.ClientResponse]: async with aiohttp.ClientSession() as session: - async with session.request(method, url, ssl=get_ssl_context(), **kwargs) as response: + async with session.request(method, url, **kwargs) as response: yield response diff --git a/tests/integration/other/test_exchange_rate_manager.py b/tests/integration/other/test_exchange_rate_manager.py index 6688d4a14..c8346f8ee 100644 --- a/tests/integration/other/test_exchange_rate_manager.py +++ b/tests/integration/other/test_exchange_rate_manager.py @@ -5,7 +5,6 @@ from lbry.extras.daemon.exchange_rate_manager import ExchangeRate, ExchangeRateM class TestExchangeRateManager(AsyncioTestCase): async def test_exchange_rate_manager(self): - # TODO: re-enable cryptonator.com manager = ExchangeRateManager(FEEDS) manager.start() self.addCleanup(manager.stop) @@ -16,6 +15,10 @@ class TestExchangeRateManager(AsyncioTestCase): for feed in manager.market_feeds: self.assertTrue(feed.is_online) self.assertIsInstance(feed.rate, ExchangeRate) - lbc = manager.convert_currency('USD', 'LBC', Decimal('0.01')) - self.assertGreaterEqual(lbc, 0.01) + # print(f'{feed.name} - {feed.market} - {feed.rate.spot}') + lbc = manager.convert_currency('USD', 'LBC', Decimal('1.0')) + self.assertGreaterEqual(lbc, 2.0) self.assertLessEqual(lbc, 10.0) + lbc = manager.convert_currency('BTC', 'LBC', Decimal('0.01')) + self.assertGreaterEqual(lbc, 1_000) + self.assertLessEqual(lbc, 4_000)