diff --git a/config/settings/base.py b/config/settings/base.py index 39e8a9766..1ab64641a 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -496,10 +496,6 @@ "TOKENS_ERC20_GET_BALANCES_BATCH", default=2_000 ) # Number of tokens to get balances from in the same request. From 2_500 some nodes raise HTTP 413 -TOKEN_ETH_PRICE_TTL = env.int( - "TOKEN_ETH_PRICE_TTL", default=60 * 30 # 30 minutes -) # Expiration time for token eth price - # Notifications # ------------------------------------------------------------------------------ SLACK_API_WEBHOOK = env("SLACK_API_WEBHOOK", default=None) diff --git a/safe_transaction_service/tokens/clients/__init__.py b/safe_transaction_service/tokens/clients/__init__.py index 7fddb40b4..494386c22 100644 --- a/safe_transaction_service/tokens/clients/__init__.py +++ b/safe_transaction_service/tokens/clients/__init__.py @@ -1,8 +1,4 @@ # flake8: noqa F401 -from .binance_client import BinanceClient from .coingecko_client import CoingeckoClient from .coinmarketcap_client import CoinMarketCapClient, CoinMarketCapToken -from .exceptions import CannotGetPrice from .kleros_client import KlerosClient, KlerosToken -from .kraken_client import KrakenClient -from .kucoin_client import KucoinClient diff --git a/safe_transaction_service/tokens/clients/binance_client.py b/safe_transaction_service/tokens/clients/binance_client.py deleted file mode 100644 index 7581036e6..000000000 --- a/safe_transaction_service/tokens/clients/binance_client.py +++ /dev/null @@ -1,47 +0,0 @@ -import logging - -from .base_client import BaseHTTPClient -from .exceptions import CannotGetPrice - -logger = logging.getLogger(__name__) - - -class BinanceClient(BaseHTTPClient): # pragma: no cover - def _get_price(self, symbol: str) -> float: - url = f"https://api.binance.com/api/v3/avgPrice?symbol={symbol}" - try: - response = self.http_session.get(url, timeout=self.request_timeout) - api_json = response.json() - if not response.ok: - logger.warning("Cannot get price from url=%s", url) - raise CannotGetPrice(api_json.get("msg")) - - price = float(api_json["price"]) - if not price: - raise CannotGetPrice(f"Price from url={url} is {price}") - return price - except (ValueError, IOError) as e: - raise CannotGetPrice from e - - def get_ada_usd_price(self) -> float: - return self._get_price("ADAUSDT") - - def get_aurora_usd_price(self): - return self._get_price("NEARUSDT") - - def get_bnb_usd_price(self) -> float: - return self._get_price("BNBUSDT") - - def get_ether_usd_price(self) -> float: - """ - :return: current USD price for Ethereum - :raises: CannotGetPrice - """ - return self._get_price("ETHUSDT") - - def get_matic_usd_price(self) -> float: - """ - :return: current USD price for MATIC - :raises: CannotGetPrice - """ - return self._get_price("MATICUSDT") diff --git a/safe_transaction_service/tokens/clients/coingecko_client.py b/safe_transaction_service/tokens/clients/coingecko_client.py index a327930ce..9451bb9f5 100644 --- a/safe_transaction_service/tokens/clients/coingecko_client.py +++ b/safe_transaction_service/tokens/clients/coingecko_client.py @@ -9,7 +9,6 @@ from safe_transaction_service.tokens.clients.base_client import BaseHTTPClient from safe_transaction_service.tokens.clients.exceptions import ( - CannotGetPrice, Coingecko404, CoingeckoRateLimitError, CoingeckoRequestError, @@ -61,44 +60,6 @@ def _do_request(self, url: str) -> Dict[str, Any]: logger.warning("Problem fetching %s", url) raise CoingeckoRequestError from e - def _get_price(self, url: str, name: str): - try: - result = self._do_request(url) - - # Result is returned with lowercased `name` (if querying by contract address, then `token_address`) - price = result.get(name) - if price and price.get("usd"): - return price["usd"] - else: - raise CannotGetPrice(f"Price from url={url} is {price}") - except CoingeckoRequestError as e: - raise CannotGetPrice( - f"Cannot get price from Coingecko for token={name}" - ) from e - - def get_price(self, name: str) -> float: - """ - :param name: coin name - :return: usd price for token name, 0. if not found - """ - name = name.lower() - url = urljoin( - self.base_url, f"/api/v3/simple/price?ids={name}&vs_currencies=usd" - ) - return self._get_price(url, name) - - def get_token_price(self, token_address: ChecksumAddress) -> float: - """ - :param token_address: - :return: usd price for token address, 0. if not found - """ - token_address = token_address.lower() - url = urljoin( - self.base_url, - f"api/v3/simple/token_price/{self.asset_platform}?contract_addresses={token_address}&vs_currencies=usd", - ) - return self._get_price(url, token_address) - @lru_cache(maxsize=128) def get_token_info( self, token_address: ChecksumAddress @@ -117,36 +78,3 @@ def get_token_logo_url(self, token_address: ChecksumAddress) -> Optional[str]: token_info = self.get_token_info(token_address) if token_info: return token_info["image"]["large"] - - def get_ada_usd_price(self) -> float: - return self.get_price("cardano") - - def get_avax_usd_price(self) -> float: - return self.get_price("avalanche-2") - - def get_aoa_usd_price(self) -> float: - return self.get_price("aurora") - - def get_bnb_usd_price(self) -> float: - return self.get_price("binancecoin") - - def get_ewt_usd_price(self) -> float: - return self.get_price("energy-web-token") - - def get_matic_usd_price(self) -> float: - return self.get_price("matic-network") - - def get_gather_usd_price(self) -> float: - return self.get_price("gather") - - def get_fuse_usd_price(self) -> float: - return self.get_price("fuse-network-token") - - def get_kcs_usd_price(self) -> float: - return self.get_price("kucoin-shares") - - def get_metis_usd_price(self) -> float: - return self.get_price("metis-token") - - def get_mtr_usd_price(self) -> float: - return self.get_price("meter-stable") diff --git a/safe_transaction_service/tokens/clients/exceptions.py b/safe_transaction_service/tokens/clients/exceptions.py index 15046cf3d..ca0d1e29e 100644 --- a/safe_transaction_service/tokens/clients/exceptions.py +++ b/safe_transaction_service/tokens/clients/exceptions.py @@ -15,7 +15,3 @@ class CoingeckoRateLimitError(CoingeckoRequestError): } } """ - - -class CannotGetPrice(CoingeckoRequestError): - pass diff --git a/safe_transaction_service/tokens/clients/kraken_client.py b/safe_transaction_service/tokens/clients/kraken_client.py deleted file mode 100644 index 8e593c954..000000000 --- a/safe_transaction_service/tokens/clients/kraken_client.py +++ /dev/null @@ -1,72 +0,0 @@ -import logging - -from .base_client import BaseHTTPClient -from .exceptions import CannotGetPrice - -logger = logging.getLogger(__name__) - - -class KrakenClient(BaseHTTPClient): - def _get_price(self, symbol: str) -> float: - url = f"https://api.kraken.com/0/public/Ticker?pair={symbol}" - try: - response = self.http_session.get(url, timeout=self.request_timeout) - api_json = response.json() - error = api_json.get("error") - if not response.ok or error: - logger.warning("Cannot get price from url=%s", url) - raise CannotGetPrice(str(api_json["error"])) - - result = api_json["result"] - for new_ticker in result: - price = float(result[new_ticker]["c"][0]) - if not price: - raise CannotGetPrice(f"Price from url={url} is {price}") - return price - except (ValueError, IOError) as e: - raise CannotGetPrice from e - - def get_ada_usd_price(self) -> float: - return self._get_price("ADAUSD") - - def get_avax_usd_price(self) -> float: - """ - :return: current USD price for AVAX - :raises: CannotGetPrice - """ - return self._get_price("AVAXUSD") - - def get_dai_usd_price(self) -> float: - """ - :return: current USD price for DAI - :raises: CannotGetPrice - """ - return self._get_price("DAIUSD") - - def get_ether_usd_price(self) -> float: - """ - :return: current USD price for Ethereum - :raises: CannotGetPrice - """ - return self._get_price("ETHUSD") - - def get_matic_usd_price(self): - """ - :return: current USD price for MATIC - :raises: CannotGetPrice - """ - return self._get_price("MATICUSD") - - def get_ewt_usd_price(self) -> float: - """ - :return: current USD price for Energy Web Token - :raises: CannotGetPrice - """ - return self._get_price("EWTUSD") - - def get_algo_usd_price(self): - """ - :return: current USD price for Algorand - :raises: CannotGetPrice - """ - return self._get_price("ALGOUSD") diff --git a/safe_transaction_service/tokens/clients/kucoin_client.py b/safe_transaction_service/tokens/clients/kucoin_client.py deleted file mode 100644 index b4aa4a465..000000000 --- a/safe_transaction_service/tokens/clients/kucoin_client.py +++ /dev/null @@ -1,89 +0,0 @@ -import logging - -from .base_client import BaseHTTPClient -from .exceptions import CannotGetPrice - -logger = logging.getLogger(__name__) - - -class KucoinClient(BaseHTTPClient): - def _get_price(self, symbol: str): - url = f"https://api.kucoin.com/api/v1/market/orderbook/level1?symbol={symbol}" - - try: - response = self.http_session.get(url, timeout=self.request_timeout) - result = response.json() - return float(result["data"]["price"]) - except (ValueError, IOError) as e: - logger.warning("Cannot get price from url=%s", url) - raise CannotGetPrice from e - - def get_ether_usd_price(self) -> float: - """ - :return: current USD price for ETH Coin - :raises: CannotGetPrice - """ - return self._get_price("ETH-USDT") - - def get_aurora_usd_price(self) -> float: - """ - :return: current USD price for Aurora Coin - :raises: CannotGetPrice - """ - return self._get_price("AURORA-USDT") - - def get_bnb_usd_price(self) -> float: - """ - :return: current USD price for Binance Coin - :raises: CannotGetPrice - """ - return self._get_price("BNB-USDT") - - def get_celo_usd_price(self) -> float: - """ - :return: current USD price for Celo - :raises: CannotGetPrice - """ - return self._get_price("CELO-USDT") - - def get_cro_usd_price(self) -> float: - """ - :return: current USD price for Cronos - :raises: CannotGetPrice - """ - return self._get_price("CRO-USDT") - - def get_ewt_usd_price(self) -> float: - """ - :return: current USD price for Energy Web Token - :raises: CannotGetPrice - """ - return self._get_price("EWT-USDT") - - def get_kcs_usd_price(self) -> float: - """ - :return: current USD price for KuCoin Token - :raises: CannotGetPrice - """ - return self._get_price("KCS-USDT") - - def get_matic_usd_price(self) -> float: - """ - :return: current USD price for MATIC Token - :raises: CannotGetPrice - """ - return self._get_price("MATIC-USDT") - - def get_xdc_usd_price(self) -> float: - """ - :return: current USD price for XDC Token - :raises: CannotGetPrice - """ - return self._get_price("XDC-USDT") - - def get_ftm_usd_price(self) -> float: - """ - :return: current USD price for FTM Token - :raises: CannotGetPrice - """ - return self._get_price("FTM-USDT") diff --git a/safe_transaction_service/tokens/signals.py b/safe_transaction_service/tokens/signals.py index d7b01b660..3a21b786c 100644 --- a/safe_transaction_service/tokens/signals.py +++ b/safe_transaction_service/tokens/signals.py @@ -11,7 +11,6 @@ ) from .models import Token -from .services import PriceServiceProvider logger = logging.getLogger(__name__) @@ -34,9 +33,3 @@ def clear_cache(sender: Type[Model], instance: Token, created: bool, **kwargs) - collectibles_service = CollectiblesServiceProvider() collectibles_service.cache_token_info.clear() - - price_service = PriceServiceProvider() - price_service.cache_token_eth_value.clear() - price_service.cache_token_info.clear() - price_service.cache_token_usd_value.clear() - price_service.cache_underlying_token.clear() diff --git a/safe_transaction_service/tokens/tests/clients/test_clients.py b/safe_transaction_service/tokens/tests/clients/test_clients.py deleted file mode 100644 index d83b34829..000000000 --- a/safe_transaction_service/tokens/tests/clients/test_clients.py +++ /dev/null @@ -1,88 +0,0 @@ -from unittest import mock - -from django.test import TestCase - -from requests import Session - -from gnosis.eth.tests.utils import just_test_if_mainnet_node - -from ...clients import CannotGetPrice, CoingeckoClient, KrakenClient, KucoinClient - - -class TestClients(TestCase): - def test_get_bnb_usd_price(self) -> float: - just_test_if_mainnet_node() - kucoin_client = KucoinClient() - coingecko_client = CoingeckoClient() - - price = kucoin_client.get_bnb_usd_price() - self.assertIsInstance(price, float) - self.assertGreater(price, 0) - - price = coingecko_client.get_bnb_usd_price() - self.assertIsInstance(price, float) - self.assertGreater(price, 0) - - def test_get_dai_usd_price_kraken(self) -> float: - just_test_if_mainnet_node() - kraken_client = KrakenClient() - - # Kraken is used - price = kraken_client.get_dai_usd_price() - self.assertIsInstance(price, float) - self.assertGreater(price, 0) - - def test_get_ether_usd_price_kraken(self): - just_test_if_mainnet_node() - kraken_client = KrakenClient() - - # Kraken is used - eth_usd_price = kraken_client.get_ether_usd_price() - self.assertIsInstance(eth_usd_price, float) - self.assertGreater(eth_usd_price, 0) - - def test_get_ewt_usd_price_kraken(self) -> float: - just_test_if_mainnet_node() - kraken_client = KrakenClient() - - # Kraken is used - price = kraken_client.get_ewt_usd_price() - self.assertIsInstance(price, float) - self.assertGreater(price, 0) - - def test_get_ether_usd_price_kucoin(self): - just_test_if_mainnet_node() - kucoin_client = KucoinClient() - - eth_usd_price = kucoin_client.get_ether_usd_price() - self.assertIsInstance(eth_usd_price, float) - self.assertGreater(eth_usd_price, 0) - - def test_get_matic_usd_price(self) -> float: - just_test_if_mainnet_node() - - for provider in [KucoinClient(), KrakenClient(), CoingeckoClient()]: - with self.subTest(provider=provider): - price = provider.get_matic_usd_price() - self.assertIsInstance(price, float) - self.assertGreater(price, 0) - - def test_get_ewt_usd_price_coingecko(self) -> float: - just_test_if_mainnet_node() - coingecko_client = CoingeckoClient() - - price = coingecko_client.get_ewt_usd_price() - self.assertIsInstance(price, float) - self.assertGreater(price, 0) - - def test_get_ewt_usd_price_kucoin(self) -> float: - just_test_if_mainnet_node() - kucoin_client = KucoinClient() - - price = kucoin_client.get_ewt_usd_price() - self.assertIsInstance(price, float) - self.assertGreater(price, 0) - - with mock.patch.object(Session, "get", side_effect=IOError("Connection Error")): - with self.assertRaises(CannotGetPrice): - kucoin_client.get_ewt_usd_price() diff --git a/safe_transaction_service/tokens/tests/clients/test_coingecko_client.py b/safe_transaction_service/tokens/tests/clients/test_coingecko_client.py index a08ae820e..00a799d0f 100644 --- a/safe_transaction_service/tokens/tests/clients/test_coingecko_client.py +++ b/safe_transaction_service/tokens/tests/clients/test_coingecko_client.py @@ -4,7 +4,6 @@ from safe_transaction_service.history.tests.utils import skip_on -from ...clients import CannotGetPrice from ...clients.coingecko_client import CoingeckoClient from ...clients.exceptions import CoingeckoRateLimitError @@ -13,38 +12,6 @@ class TestCoingeckoClient(TestCase): GNO_TOKEN_ADDRESS = "0x6810e776880C02933D47DB1b9fc05908e5386b96" GNO_GNOSIS_CHAIN_ADDRESS = "0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb" - @skip_on(CannotGetPrice, reason="Cannot get price from Coingecko") - def test_coingecko_client(self): - self.assertTrue(CoingeckoClient.supports_network(EthereumNetwork.MAINNET)) - self.assertTrue( - CoingeckoClient.supports_network( - EthereumNetwork.BINANCE_SMART_CHAIN_MAINNET - ) - ) - self.assertTrue(CoingeckoClient.supports_network(EthereumNetwork.POLYGON)) - self.assertTrue(CoingeckoClient.supports_network(EthereumNetwork.GNOSIS)) - - # Test Mainnet - coingecko_client = CoingeckoClient() - non_existing_token_address = "0xda2f8b8386302C354a90DB670E40beA3563AF454" - self.assertGreater(coingecko_client.get_token_price(self.GNO_TOKEN_ADDRESS), 0) - with self.assertRaises(CannotGetPrice): - coingecko_client.get_token_price(non_existing_token_address) - - # Test Binance - bsc_coingecko_client = CoingeckoClient( - EthereumNetwork.BINANCE_SMART_CHAIN_MAINNET - ) - binance_peg_ethereum_address = "0x2170Ed0880ac9A755fd29B2688956BD959F933F8" - self.assertGreater( - bsc_coingecko_client.get_token_price(binance_peg_ethereum_address), 0 - ) - - # Test Polygon - polygon_coingecko_client = CoingeckoClient(EthereumNetwork.POLYGON) - bnb_pos_address = "0xb33EaAd8d922B1083446DC23f610c2567fB5180f" - self.assertGreater(polygon_coingecko_client.get_token_price(bnb_pos_address), 0) - @skip_on(CoingeckoRateLimitError, reason="Coingecko rate limit reached") def test_get_logo_url(self): # Test Mainnet diff --git a/safe_transaction_service/tokens/tests/test_views.py b/safe_transaction_service/tokens/tests/test_views.py index 6a81c771c..d0e852ec8 100644 --- a/safe_transaction_service/tokens/tests/test_views.py +++ b/safe_transaction_service/tokens/tests/test_views.py @@ -98,10 +98,3 @@ def test_tokens_view(self): } ], ) - - def test_token_price_view(self): - random_address = Account.create().address - response = self.client.get( - reverse("v1:tokens:price-usd", args=(random_address,)) - ) - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) diff --git a/safe_transaction_service/tokens/urls.py b/safe_transaction_service/tokens/urls.py index 5b636d17e..60e46f12e 100644 --- a/safe_transaction_service/tokens/urls.py +++ b/safe_transaction_service/tokens/urls.py @@ -7,5 +7,4 @@ urlpatterns = [ path("", views.TokensView.as_view(), name="list"), path("/", views.TokenView.as_view(), name="detail"), - path("/prices/usd/", views.TokenPriceView.as_view(), name="price-usd"), ] diff --git a/safe_transaction_service/tokens/views.py b/safe_transaction_service/tokens/views.py index 86955d00c..006fface1 100644 --- a/safe_transaction_service/tokens/views.py +++ b/safe_transaction_service/tokens/views.py @@ -5,7 +5,6 @@ from rest_framework import response, status from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.generics import ListAPIView, RetrieveAPIView -from rest_framework.response import Response from gnosis.eth.utils import fast_is_checksum_address @@ -50,4 +49,3 @@ class TokensView(ListAPIView): @method_decorator(cache_page(60 * 15)) # Cache 15 minutes def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) -