diff --git a/infra/price-pusher/config/config.mainnet.yaml b/infra/price-pusher/config/config.mainnet.yaml index 87e8853b..0342abc0 100644 --- a/infra/price-pusher/config/config.mainnet.yaml +++ b/infra/price-pusher/config/config.mainnet.yaml @@ -20,6 +20,7 @@ - ZEND/USD - NSTR/USD - EKUBO/USD + - BROTHER/USD time_difference: 600 price_deviation: 0.025 diff --git a/infra/price-pusher/config/config.sepolia.yaml b/infra/price-pusher/config/config.sepolia.yaml index eb58689a..5b90aafe 100644 --- a/infra/price-pusher/config/config.sepolia.yaml +++ b/infra/price-pusher/config/config.sepolia.yaml @@ -20,6 +20,7 @@ - ZEND/USD - NSTR/USD - EKUBO/USD + - BROTHER/USD - LUSD/USD time_difference: 120 price_deviation: 0.005 diff --git a/pragma-sdk/docs/source/quickstart.rst b/pragma-sdk/docs/source/quickstart.rst index f4bd316d..95e7e0ad 100644 --- a/pragma-sdk/docs/source/quickstart.rst +++ b/pragma-sdk/docs/source/quickstart.rst @@ -122,7 +122,7 @@ An API key is currently needed to interact with the off-chain API. You can get o ) # Get 1min OHLC data - entries = await poc.get_ohlc( + entries = await pac.get_ohlc( 'BTC/USD', None, Interval.ONE_MINUTE, diff --git a/pragma-sdk/pragma_sdk/common/fetchers/fetchers/__init__.py b/pragma-sdk/pragma_sdk/common/fetchers/fetchers/__init__.py index 606be3f4..b6d192d6 100644 --- a/pragma-sdk/pragma_sdk/common/fetchers/fetchers/__init__.py +++ b/pragma-sdk/pragma_sdk/common/fetchers/fetchers/__init__.py @@ -8,7 +8,6 @@ from pragma_sdk.common.fetchers.fetchers.indexcoop import IndexCoopFetcher from pragma_sdk.common.fetchers.fetchers.kucoin import KucoinFetcher from pragma_sdk.common.fetchers.fetchers.okx import OkxFetcher -from pragma_sdk.common.fetchers.fetchers.propeller import PropellerFetcher from pragma_sdk.common.fetchers.fetchers.ekubo import EkuboFetcher from pragma_sdk.common.fetchers.fetchers.mexc import MEXCFetcher from pragma_sdk.common.fetchers.fetchers.gateio import GateioFetcher @@ -25,7 +24,6 @@ "IndexCoopFetcher", "KucoinFetcher", "OkxFetcher", - "PropellerFetcher", "EkuboFetcher", "MEXCFetcher", "GateioFetcher", diff --git a/pragma-sdk/pragma_sdk/common/fetchers/fetchers/gecko.py b/pragma-sdk/pragma_sdk/common/fetchers/fetchers/gecko.py index f1ea64ea..4a76ee41 100644 --- a/pragma-sdk/pragma_sdk/common/fetchers/fetchers/gecko.py +++ b/pragma-sdk/pragma_sdk/common/fetchers/fetchers/gecko.py @@ -66,6 +66,10 @@ "DPI": ("eth", "0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b"), "MVI": ("eth", "0x72e364f2abdc788b7e918bc238b21f109cd634d7"), "NSTR": ("eth", "0x610dbd98a28ebba525e9926b6aaf88f9159edbfd"), + "BROTHER": ( + "starknet-alpha", + "0x3b405a98c9e795d427fe82cdeeeed803f221b52471e3a757574a2b4180793ee", + ), } diff --git a/pragma-sdk/pragma_sdk/common/fetchers/fetchers/propeller.py b/pragma-sdk/pragma_sdk/common/fetchers/fetchers/propeller.py deleted file mode 100644 index dc6c5fbd..00000000 --- a/pragma-sdk/pragma_sdk/common/fetchers/fetchers/propeller.py +++ /dev/null @@ -1,145 +0,0 @@ -import asyncio -import json -import time -from typing import Any, Dict, List, Optional - -from aiohttp import ClientSession - -from pragma_sdk.common.types.pair import Pair -from pragma_sdk.common.types.entry import Entry, SpotEntry -from pragma_sdk.common.exceptions import PublisherFetchError -from pragma_sdk.common.fetchers.interface import FetcherInterfaceT -from pragma_sdk.common.types.currency import Currency -from pragma_sdk.common.logging import get_pragma_sdk_logger - -logger = get_pragma_sdk_logger() - -SELL_AMOUNTS = [1, 10, 100, 1000] - -ASSET_MAPPING: Dict[str, str] = { - "ETH": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "USDC": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "USD": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", # FIXME: Unsafe - "USDT": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "DAI": "0x6B175474E89094C44Da98b954EedeAC495271d0F", - "WBTC": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "BTC": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", # FIXME: Unsafe - "WSTETH": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - "ZEND": "0xb2606492712d311be8f41d940afe8ce742a52d442", - "DPI": "0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b", - "WETH": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "MVI": "0x72e364f2abdc788b7e918bc238b21f109cd634d7", -} - - -# Propeller requires a payload to be sent with the request -# The payload is a list of orders, each with a sell_token, buy_token, and sell_amount -# The sell_amount is the amount of the sell_token to be sold -# The buy_token is the token to be bought -# The sell_token is the token to be sold -def build_payload(sell_token: Currency, buy_token: Currency) -> Any: - address_0 = ASSET_MAPPING.get(sell_token.id) - address_1 = ASSET_MAPPING.get(buy_token.id) - sell_amount = 10**sell_token.decimals * SELL_AMOUNTS[0] - if address_0 is None or address_1 is None: - raise PublisherFetchError( - f"Unknown price pair, do not know how to query Propeller for {sell_token}/{buy_token}" - ) - return { - "orders": [ - { - "sell_token": address_0, - "buy_token": address_1, - "sell_amount": sell_amount, - } - ], - } - - -class PropellerFetcher(FetcherInterfaceT): - BASE_URL: str = ( - "https://api.propellerheads.xyz/partner/v2/solver/quote?blockchain=ethereum" - ) - SOURCE: str = "PROPELLER" - - async def fetch_pair( - self, pair: Pair, session: ClientSession - ) -> SpotEntry | PublisherFetchError: - url = self.format_url() - try: - payload = build_payload(pair.base_currency, pair.quote_currency) - except PublisherFetchError as err: - return err - - async with session.post(url, headers=self.headers, json=payload) as resp: - if resp.status == 404: - return PublisherFetchError( - f"No data found for {pair} from {self.publisher}" - ) - - if resp.status == 403: - return PublisherFetchError( - "Unauthorized: Please provide an API Key to use PropellerFetcher" - ) - - content_type = resp.content_type - if content_type and "json" in content_type: - text = await resp.text() - result = json.loads(text) - else: - raise ValueError(f"PROPELLER: Unexpected content type: {content_type}") - - if "error" in result and result["error"] == "Invalid Symbols Pair": - return PublisherFetchError( - f"No data found for {pair} from {self.publisher}" - ) - - return self._construct(pair, result) - - async def fetch( - self, session: ClientSession - ) -> List[Entry | PublisherFetchError | BaseException]: - entries = [] - for pair in self.pairs: - entries.append(asyncio.ensure_future(self.fetch_pair(pair, session))) - return list(await asyncio.gather(*entries, return_exceptions=True)) - - def format_url(self, pair: Optional[Pair] = None) -> str: - url = self.BASE_URL - return url - - def _construct( - self, - pair: Pair, - result: Any, - ) -> SpotEntry: - mid_prices = [] - - for quotes, buy_tokens, sell_tokens in zip( - result["quotes"], result["buy_tokens"], result["sell_tokens"] - ): - sell_amount = float(quotes["sell_amount"]) - buy_amount = float(quotes["buy_amount"]) - sell_decimals = int(sell_tokens["decimals"]) - buy_decimals = int(buy_tokens["decimals"]) - # Take the mid price - mid_price = (buy_amount / sell_amount) * 10 ** ( - sell_decimals - buy_decimals - ) - mid_prices.append(mid_price) - - price = sum(mid_prices) / len(mid_prices) - price_int = int(price * (10 ** pair.decimals())) - - timestamp = int(time.time()) - - logger.debug("Fetched price %d for %s from Propeller", price_int, pair) - - return SpotEntry( - pair_id=pair.id, - price=price_int, - timestamp=timestamp, - source=self.SOURCE, - volume=0, - publisher=self.publisher, - ) diff --git a/pragma-sdk/pragma_sdk/supported_assets.yaml b/pragma-sdk/pragma_sdk/supported_assets.yaml index 927c15c6..bfbfd904 100644 --- a/pragma-sdk/pragma_sdk/supported_assets.yaml +++ b/pragma-sdk/pragma_sdk/supported_assets.yaml @@ -240,3 +240,9 @@ decimals: 18 ticker: 'ONDO' coingecko_id: 'ondo' + +- name: 'STARKNET BROTHER' + decimals: 18 + ticker: 'BROTHER' + coingecko_id: 'starknet-brother' + starknet_address: '0x3b405a98c9e795d427fe82cdeeeed803f221b52471e3a757574a2b4180793ee' diff --git a/pragma-sdk/tests/integration/fetchers/fetcher_configs.py b/pragma-sdk/tests/integration/fetchers/fetcher_configs.py index 6f845cfb..0c29b1ab 100644 --- a/pragma-sdk/tests/integration/fetchers/fetcher_configs.py +++ b/pragma-sdk/tests/integration/fetchers/fetcher_configs.py @@ -5,7 +5,6 @@ CoinbaseFetcher, OkxFetcher, EkuboFetcher, - PropellerFetcher, GeckoTerminalFetcher, DexscreenerFetcher, ) @@ -121,18 +120,6 @@ PUBLISHER_NAME, ), }, - "PropellerFetcher": { - "mock_file": MOCK_DIR / "responses" / "propeller.json", - "fetcher_class": PropellerFetcher, - "name": "PROPELLER", - "expected_result": SpotEntry( - "INDEXNAME1", - int(4891252302700 * 0.5 + 0.5 * 262209039700), - 12345, - "PROPELLER", - PUBLISHER_NAME, - ), - }, } FUTURE_FETCHER_CONFIGS = { diff --git a/pragma-sdk/tests/integration/mock/responses/propeller.json b/pragma-sdk/tests/integration/mock/responses/propeller.json deleted file mode 100644 index 6d79b660..00000000 --- a/pragma-sdk/tests/integration/mock/responses/propeller.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "BTC": { - "request_id": "propeller-quote-e0b0b3fd", - "quotes": [ - { - "sell_token": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "buy_token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "sell_amount": "100000000", - "buy_amount": "48912523027", - "external_id": "0" - } - ], - "gas": 296733, - "target_address": "0x14f2b6ca0324cd2B013aD02a7D85541d215e2906", - "buy_tokens": [ - { - "symbol": "USDC", - "decimals": "6", - "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" - } - ], - "sell_tokens": [ - { - "symbol": "WBTC", - "decimals": "8", - "address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" - } - ] - }, - "ETH": { - "request_id": "propeller-quote-54c90a31", - "quotes": [ - { - "sell_token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "buy_token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "sell_amount": "1000000000000000000", - "buy_amount": "2622090397", - "external_id": "0" - } - ], - "gas": 84100, - "target_address": "0x14f2b6ca0324cd2B013aD02a7D85541d215e2906", - "buy_tokens": [ - { - "symbol": "USDC", - "decimals": "6", - "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" - } - ], - "sell_tokens": [ - { - "symbol": "WETH", - "decimals": "18", - "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" - } - ] - } -} \ No newline at end of file diff --git a/price-pusher/price_pusher/configs/fetchers.py b/price-pusher/price_pusher/configs/fetchers.py index 6f0d7630..61a191c2 100644 --- a/price-pusher/price_pusher/configs/fetchers.py +++ b/price-pusher/price_pusher/configs/fetchers.py @@ -12,7 +12,6 @@ KucoinFetcher, OkxFetcher, BinanceFetcher, - PropellerFetcher, EkuboFetcher, DexscreenerFetcher, ) @@ -32,7 +31,6 @@ KucoinFetcher, BybitFetcher, BinanceFetcher, - PropellerFetcher, EkuboFetcher, DexscreenerFetcher, ] @@ -56,7 +54,6 @@ class FetcherWithApiKeyConfig: # Configuration for fetchers that may require API keys. FETCHERS_WITH_API_KEY: Dict[FetcherInterfaceT, FetcherWithApiKeyConfig] = { - PropellerFetcher: FetcherWithApiKeyConfig(env_api_key="PROPELLER_API_KEY"), DefillamaFetcher: FetcherWithApiKeyConfig( env_api_key="DEFI_LLAMA_API_KEY", optional=True ),