diff --git a/pyinjective/async_client.py b/pyinjective/async_client.py index f437db7f..50eddecf 100644 --- a/pyinjective/async_client.py +++ b/pyinjective/async_client.py @@ -1,4 +1,5 @@ import asyncio +import base64 import time from copy import deepcopy from decimal import Decimal @@ -2497,6 +2498,51 @@ async def composer(self): tokens=await self.all_tokens(), ) + async def initialize_tokens_from_chain_denoms(self): + # force initialization of markets and tokens + await self.all_tokens() + + all_denoms_metadata = [] + + query_result = await self.fetch_denoms_metadata() + + all_denoms_metadata.extend(query_result.get("metadatas", [])) + next_key = query_result.get("pagination", {}).get("nextKey", "") + + while next_key != "": + query_result = await self.fetch_denoms_metadata(pagination=PaginationOption(key=next_key)) + + all_denoms_metadata.extend(query_result.get("metadatas", [])) + result_next_key = query_result.get("pagination", {}).get("nextKey", "") + next_key = base64.b64decode(result_next_key).decode() + + for token_metadata in all_denoms_metadata: + symbol = token_metadata["symbol"] + denom = token_metadata["base"] + + if denom != "" and symbol != "" and denom not in self._tokens_by_denom: + name = token_metadata["name"] or symbol + decimals = max({denom_unit["exponent"] for denom_unit in token_metadata["denomUnits"]}) + + unique_symbol = denom + for symbol_candidate in [symbol, name]: + if symbol_candidate not in self._tokens_by_symbol: + unique_symbol = symbol_candidate + break + + token = Token( + name=name, + symbol=symbol, + denom=denom, + address="", + decimals=decimals, + logo=token_metadata["uri"], + updated=-1, + ) + + self._tokens_by_denom[denom] = token + self._tokens_by_symbol[unique_symbol] = token + async def _initialize_tokens_and_markets(self): spot_markets = dict() derivative_markets = dict() diff --git a/pyinjective/client/model/pagination.py b/pyinjective/client/model/pagination.py index f3607c17..906a6060 100644 --- a/pyinjective/client/model/pagination.py +++ b/pyinjective/client/model/pagination.py @@ -31,7 +31,7 @@ def create_pagination_request(self) -> pagination_pb.PageRequest: page_request = pagination_pb.PageRequest() if self.key is not None: - page_request.key = bytes.fromhex(self.key) + page_request.key = self.key.encode() if self.skip is not None: page_request.offset = self.skip if self.limit is not None: diff --git a/tests/rpc_fixtures/markets_fixtures.py b/tests/rpc_fixtures/markets_fixtures.py index 69f835df..9c3a56ad 100644 --- a/tests/rpc_fixtures/markets_fixtures.py +++ b/tests/rpc_fixtures/markets_fixtures.py @@ -1,6 +1,32 @@ import pytest +@pytest.fixture +def smart_denom_metadata(): + from pyinjective.proto.cosmos.bank.v1beta1 import bank_pb2 as bank_pb + + first_denom_unit = bank_pb.DenomUnit( + denom="factory/inj105ujajd95znwjvcy3hwcz80pgy8tc6v77spur0/SMART", exponent=0, aliases=["microSMART"] + ) + second_denom_unit = bank_pb.DenomUnit(denom="SMART", exponent=6, aliases=["SMART"]) + metadata = bank_pb.Metadata( + description="SMART", + denom_units=[first_denom_unit, second_denom_unit], + base="factory/inj105ujajd95znwjvcy3hwcz80pgy8tc6v77spur0/SMART", + display="SMART", + name="SMART", + symbol="SMART", + uri=( + "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/" + "Flag_of_the_People%27s_Republic_of_China.svg/" + "2560px-Flag_of_the_People%27s_Republic_of_China.svg.png" + ), + uri_hash="", + ) + + return metadata + + @pytest.fixture def inj_token_meta(): from pyinjective.proto.exchange.injective_spot_exchange_rpc_pb2 import TokenMeta diff --git a/tests/test_async_client.py b/tests/test_async_client.py index 7780e088..b7b597ed 100644 --- a/tests/test_async_client.py +++ b/tests/test_async_client.py @@ -4,22 +4,31 @@ from pyinjective.async_client import AsyncClient from pyinjective.core.network import Network +from pyinjective.proto.cosmos.bank.v1beta1 import query_pb2 as bank_query_pb +from pyinjective.proto.cosmos.base.query.v1beta1 import pagination_pb2 as pagination_pb from pyinjective.proto.exchange import injective_derivative_exchange_rpc_pb2, injective_spot_exchange_rpc_pb2 +from tests.client.chain.grpc.configurable_bank_query_servicer import ConfigurableBankQueryServicer from tests.client.indexer.configurable_derivative_query_servicer import ConfigurableDerivativeQueryServicer from tests.client.indexer.configurable_spot_query_servicer import ConfigurableSpotQueryServicer -from tests.rpc_fixtures.markets_fixtures import ape_token_meta # noqa: F401 -from tests.rpc_fixtures.markets_fixtures import ape_usdt_spot_market_meta # noqa: F401 -from tests.rpc_fixtures.markets_fixtures import btc_usdt_perp_market_meta # noqa: F401 -from tests.rpc_fixtures.markets_fixtures import inj_token_meta # noqa: F401 -from tests.rpc_fixtures.markets_fixtures import inj_usdt_spot_market_meta # noqa: F401 -from tests.rpc_fixtures.markets_fixtures import usdt_perp_token_meta # noqa: F401 -from tests.rpc_fixtures.markets_fixtures import usdt_token_meta # noqa: F401 -from tests.rpc_fixtures.markets_fixtures import ( # noqa: F401; noqa: F401; noqa: F401 +from tests.rpc_fixtures.markets_fixtures import ( # noqa: F401 + ape_token_meta, + ape_usdt_spot_market_meta, + btc_usdt_perp_market_meta, first_match_bet_market_meta, + inj_token_meta, + inj_usdt_spot_market_meta, + smart_denom_metadata, + usdt_perp_token_meta, + usdt_token_meta, usdt_token_meta_second_denom, ) +@pytest.fixture +def bank_servicer(): + return ConfigurableBankQueryServicer() + + @pytest.fixture def spot_servicer(): return ConfigurableSpotQueryServicer() @@ -127,3 +136,44 @@ async def test_initialize_tokens_and_markets( assert any( (first_match_bet_market_meta.market_id == market.id for market in all_binary_option_markets.values()) ) + + @pytest.mark.asyncio + async def test_initialize_tokens_from_chain_denoms( + self, + bank_servicer, + spot_servicer, + derivative_servicer, + smart_denom_metadata, + ): + pagination = pagination_pb.PageResponse( + total=1, + ) + + bank_servicer.denoms_metadata_responses.append( + bank_query_pb.QueryDenomsMetadataResponse( + metadatas=[smart_denom_metadata], + pagination=pagination, + ) + ) + + spot_servicer.markets_responses.append(injective_spot_exchange_rpc_pb2.MarketsResponse(markets=[])) + derivative_servicer.markets_responses.append(injective_derivative_exchange_rpc_pb2.MarketsResponse(markets=[])) + derivative_servicer.binary_options_markets_responses.append( + injective_derivative_exchange_rpc_pb2.BinaryOptionsMarketsResponse(markets=[]) + ) + + client = AsyncClient( + network=Network.local(), + insecure=False, + ) + + client.bank_api._stub = bank_servicer + client.exchange_spot_api._stub = spot_servicer + client.exchange_derivative_api._stub = derivative_servicer + + await client._initialize_tokens_and_markets() + await client.initialize_tokens_from_chain_denoms() + + all_tokens = await client.all_tokens() + assert 1 == len(all_tokens) + assert smart_denom_metadata.symbol in all_tokens