diff --git a/README.md b/README.md index f46ba42c..4707446e 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,9 @@ make tests ``` ### Changelogs +**0.8.4** +* Added methods to SpotMarket, DerivativeMarket and BianaryOptionMarket to translate chain prices and quantities to human-readable format. + **0.8.3** * Fix dependency issue in setup.py. diff --git a/examples/SendToInjective.py b/examples/SendToInjective.py index 5d0d1072..31614bef 100644 --- a/examples/SendToInjective.py +++ b/examples/SendToInjective.py @@ -1,15 +1,9 @@ import json -import requests - import asyncio -import logging from pyinjective.core.network import Network from pyinjective.sendtocosmos import Peggo -import importlib.resources as pkg_resources -import pyinjective - async def main() -> None: # select network: testnet, mainnet network = Network.testnet() @@ -27,8 +21,9 @@ async def main() -> None: data = '{"@type": "/injective.exchange.v1beta1.MsgDeposit","sender": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku","subaccountId": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000","amount": {"denom": "inj","amount": "1000000000000000000"}}' - import_peggo = pkg_resources.read_text(pyinjective, 'Peggo_ABI.json') - peggo_abi = json.loads(import_peggo) + with open("../pyinjective/Peggo_ABI.json") as pego_file: + peggo_data = pego_file.read() + peggo_abi = json.loads(peggo_data) peggo_composer.sendToInjective(ethereum_endpoint=ethereum_endpoint, private_key=private_key, token_contract=token_contract, receiver=receiver, amount=amount, maxFeePerGas=maxFeePerGas_Gwei, maxPriorityFeePerGas=maxPriorityFeePerGas_Gwei, data=data, peggo_abi=peggo_abi) diff --git a/examples/chain_client/48_WithdrawValidatorCommission_Rewards b/examples/chain_client/48_WithdrawValidatorCommissionAndRewards.py similarity index 100% rename from examples/chain_client/48_WithdrawValidatorCommission_Rewards rename to examples/chain_client/48_WithdrawValidatorCommissionAndRewards.py diff --git a/pyinjective/core/market.py b/pyinjective/core/market.py index 9a42b88c..2dd59fae 100644 --- a/pyinjective/core/market.py +++ b/pyinjective/core/market.py @@ -34,6 +34,13 @@ def price_to_chain_format(self, human_readable_value: Decimal) -> Decimal: return extended_chain_formatted_value + def quantity_from_chain_format(self, chain_value: Decimal) -> Decimal: + return chain_value / Decimal(f"1e{self.base_token.decimals}") + + def price_from_chain_format(self, chain_value: Decimal) -> Decimal: + decimals = self.base_token.decimals - self.quote_token.decimals + return chain_value * Decimal(f"1e{decimals}") + @dataclass(eq=True, frozen=True) class DerivativeMarket: id: str @@ -92,6 +99,15 @@ def calculate_margin_in_chain_format( return extended_chain_formatted_margin + def quantity_from_chain_format(self, chain_value: Decimal) -> Decimal: + return chain_value + + def price_from_chain_format(self, chain_value: Decimal) -> Decimal: + return chain_value * Decimal(f"1e-{self.quote_token.decimals}") + + def margin_from_chain_format(self, chain_value: Decimal) -> Decimal: + return chain_value * Decimal(f"1e-{self.quote_token.decimals}") + @dataclass(eq=True, frozen=True) class BinaryOptionMarket: id: str @@ -148,4 +164,13 @@ def calculate_margin_in_chain_format( quantized_margin = (margin // min_quantity_tick_size) * min_quantity_tick_size extended_chain_formatted_margin = quantized_margin * Decimal(f"1e{ADDITIONAL_CHAIN_FORMAT_DECIMALS}") - return extended_chain_formatted_margin \ No newline at end of file + return extended_chain_formatted_margin + + def quantity_from_chain_format(self, chain_value: Decimal, special_denom: Optional[Denom] = None) -> Decimal: + # Binary option markets do not have a base market to provide the number of decimals + decimals = 0 if special_denom is None else special_denom.base + return chain_value * Decimal(f"1e-{decimals}") + + def price_from_chain_format(self, chain_value: Decimal, special_denom: Optional[Denom] = None) -> Decimal: + decimals = self.quote_token.decimals if special_denom is None else special_denom.quote + return chain_value * Decimal(f"1e-{decimals}") diff --git a/pyinjective/sendtocosmos.py b/pyinjective/sendtocosmos.py index e19b3a4c..772ac50a 100644 --- a/pyinjective/sendtocosmos.py +++ b/pyinjective/sendtocosmos.py @@ -6,7 +6,7 @@ class Peggo: def __init__(self, network: str): self.network = network - def sendToInjective(self, ethereum_endpoint: str, private_key: str, token_contract: str, receiver: str, amount: int, + def sendToInjective(self, ethereum_endpoint: str, private_key: str, token_contract: str, receiver: str, amount: float, maxFeePerGas: int, maxPriorityFeePerGas: int, peggo_abi: str, data: str, decimals=18): if self.network == 'testnet': peggy_proxy_address = "0xd2C6753F6B1783EF0a3857275e16e79D91b539a3" diff --git a/setup.py b/setup.py index fc00456f..c7dc1bcf 100755 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ EMAIL = "achilleas@injectivelabs.com" AUTHOR = "Injective Labs" REQUIRES_PYTHON = ">=3.9" -VERSION = "0.8.3" +VERSION = "0.8.4" REQUIRED = [ "aiohttp", diff --git a/tests/core/test_market.py b/tests/core/test_market.py index 1038c51b..f3340268 100644 --- a/tests/core/test_market.py +++ b/tests/core/test_market.py @@ -39,6 +39,24 @@ def test_convert_price_to_chain_format(self, inj_usdt_spot_market: SpotMarket): assert (quantized_chain_format_value == chain_value) + def test_convert_quantity_from_chain_format(self, inj_usdt_spot_market: SpotMarket): + expected_quantity = Decimal("123.456") + + chain_format_quantity = expected_quantity * Decimal(f"1e{inj_usdt_spot_market.base_token.decimals}") + human_readable_quantity = inj_usdt_spot_market.quantity_from_chain_format(chain_value=chain_format_quantity) + + assert (expected_quantity == human_readable_quantity) + + def test_convert_price_from_chain_format(self, inj_usdt_spot_market: SpotMarket): + expected_price = Decimal("123.456") + + price_decimals = inj_usdt_spot_market.quote_token.decimals - inj_usdt_spot_market.base_token.decimals + chain_format_price = expected_price * Decimal(f"1e{price_decimals}") + human_readable_price = inj_usdt_spot_market.price_from_chain_format(chain_value=chain_format_price) + + assert (expected_price == human_readable_price) + + class TestDerivativeMarket: @@ -76,6 +94,32 @@ def test_convert_margin_to_chain_format(self, btc_usdt_perp_market: DerivativeMa assert (quantized_chain_format_value == chain_value) + def test_convert_quantity_from_chain_format(self, btc_usdt_perp_market: DerivativeMarket): + expected_quantity = Decimal("123.456") + + chain_format_quantity = expected_quantity + human_readable_quantity = btc_usdt_perp_market.quantity_from_chain_format(chain_value=chain_format_quantity) + + assert (expected_quantity == human_readable_quantity) + + def test_convert_price_from_chain_format(self, btc_usdt_perp_market: DerivativeMarket): + expected_price = Decimal("123.456") + + price_decimals = btc_usdt_perp_market.quote_token.decimals + chain_format_price = expected_price * Decimal(f"1e{price_decimals}") + human_readable_price = btc_usdt_perp_market.price_from_chain_format(chain_value=chain_format_price) + + assert (expected_price == human_readable_price) + + def test_convert_margin_from_chain_format(self, btc_usdt_perp_market: DerivativeMarket): + expected_margin = Decimal("123.456") + + price_decimals = btc_usdt_perp_market.quote_token.decimals + chain_format_margin = expected_margin * Decimal(f"1e{price_decimals}") + human_readable_margin = btc_usdt_perp_market.margin_from_chain_format(chain_value=chain_format_margin) + + assert (expected_margin == human_readable_margin) + class TestBinaryOptionMarket: def test_convert_quantity_to_chain_format_with_fixed_denom(self, first_match_bet_market: BinaryOptionMarket): @@ -211,3 +255,56 @@ def test_calculate_margin_for_sell_without_fixed_denom(self, first_match_bet_mar quantized_chain_format_margin = quantized_margin * Decimal(f"1e18") assert (quantized_chain_format_margin == chain_value) + + def test_convert_quantity_from_chain_format_with_fixed_denom(self, first_match_bet_market: BinaryOptionMarket): + original_quantity = Decimal("123.456789") + fixed_denom = Denom( + description="Fixed denom", + base=2, + quote=4, + min_quantity_tick_size=100, + min_price_tick_size=10000, + ) + + chain_formatted_quantity = original_quantity * Decimal(f"1e{fixed_denom.base}") + + human_readable_quantity = first_match_bet_market.quantity_from_chain_format( + chain_value=chain_formatted_quantity, special_denom=fixed_denom + ) + + assert (original_quantity == human_readable_quantity) + + def test_convert_quantity_from_chain_format_without_fixed_denom(self, first_match_bet_market: BinaryOptionMarket): + original_quantity = Decimal("123.456789") + + chain_formatted_quantity = original_quantity + + human_readable_quantity = first_match_bet_market.quantity_from_chain_format(chain_value=chain_formatted_quantity) + + assert (original_quantity == human_readable_quantity) + + def test_convert_price_from_chain_format_with_fixed_denom(self, first_match_bet_market: BinaryOptionMarket): + original_price = Decimal("123.456789") + fixed_denom = Denom( + description="Fixed denom", + base=2, + quote=4, + min_quantity_tick_size=100, + min_price_tick_size=10000, + ) + + chain_formatted_price = original_price * Decimal(f"1e{fixed_denom.quote}") + + human_readable_price = first_match_bet_market.price_from_chain_format( + chain_value=chain_formatted_price, special_denom=fixed_denom + ) + + assert (original_price == human_readable_price) + + def test_convert_price_from_chain_format_without_fixed_denom(self, first_match_bet_market: BinaryOptionMarket): + original_price = Decimal("123.456789") + chain_formatted_price = original_price * Decimal(f"1e{first_match_bet_market.quote_token.decimals}") + + human_readable_price = first_match_bet_market.price_from_chain_format(chain_value=chain_formatted_price) + + assert (original_price == human_readable_price) \ No newline at end of file