From 4d6ce1b20a77c776ea8dd2d2a64e9b7b460b2e76 Mon Sep 17 00:00:00 2001 From: abel Date: Tue, 20 Feb 2024 12:52:10 -0300 Subject: [PATCH] (feat) Added support for all chain exchange module queries. Included new example scripts for all the queries and unit tests. --- ...drawAddress.py => 1_SetWithdrawAddress.py} | 0 ...Reward.py => 2_WithdrawDelegatorReward.py} | 0 ...on.py => 3_WithdrawValidatorCommission.py} | 0 ...ommunityPool.py => 4_FundCommunityPool.py} | 0 .../1_ValidatorDistributionInfo.py | 0 .../2_ValidatorOutstandingRewards.py | 0 .../{ => query}/3_ValidatorCommission.py | 0 .../{ => query}/4_ValidatorSlashes.py | 0 .../{ => query}/5_DelegationRewards.py | 0 .../{ => query}/6_DelegationTotalRewards.py | 0 .../{ => query}/7_DelegatorValidators.py | 0 .../{ => query}/8_DelegatorWithdrawAddress.py | 0 .../{ => query}/9_CommunityPool.py | 0 .../10_MsgSubaccountTransfer.py} | 0 .../11_MsgBatchUpdateOrders.py} | 0 .../12_MsgRewardsOptOut.py} | 0 .../15_ExternalTransfer.py} | 0 .../16_MsgCreateBinaryOptionsLimitOrder.py} | 0 .../17_MsgCreateBinaryOptionsMarketOrder.py} | 0 .../18_MsgCancelBinaryOptionsOrder.py} | 0 .../19_MsgLiquidatePosition.py} | 0 .../1_MsgDeposit.py} | 0 .../3_MsgCreateSpotLimitOrder.py | 0 .../4_MsgCreateSpotMarketOrder.py | 0 .../{ => exchange}/5_MsgCancelSpotOrder.py | 0 .../6_MsgCreateDerivativeLimitOrder.py | 0 .../7_MsgCreateDerivativeMarketOrder.py | 0 .../8_MsgCancelDerivativeOrder.py | 0 .../9_MsgWithdraw.py} | 0 .../exchange/query/10_SpotMarkets.py | 22 + .../exchange/query/11_SpotMarket.py | 21 + .../exchange/query/12_FullSpotMarkets.py | 23 + .../exchange/query/13_FullSpotMarket.py | 22 + .../exchange/query/14_SpotOrderbook.py | 26 + .../exchange/query/15_TraderSpotOrders.py | 37 + .../query/16_AccountAddressSpotOrders.py | 35 + .../exchange/query/17_SpotOrdersByHashes.py | 38 + .../exchange/query/18_SubaccountOrders.py | 37 + .../query/19_TraderSpotTransientOrders.py | 37 + .../exchange/query/1_SubaccountDeposits.py | 34 + .../exchange/query/20_SpotMidPriceAndTOB.py | 21 + .../query/21_DerivativeMidPriceAndTOB.py | 21 + .../exchange/query/22_DerivativeOrderbook.py | 25 + .../query/23_TraderDerivativeOrders.py | 37 + .../24_AccountAddressDerivativeOrders.py | 35 + .../query/25_DerivativeOrdersByHashes.py | 38 + .../26_TraderDerivativeTransientOrders.py | 37 + .../exchange/query/27_DerivativeMarkets.py | 22 + .../exchange/query/28_DerivativeMarket.py | 21 + .../query/29_DerivativeMarketAddress.py | 21 + .../exchange/query/2_SubaccountDeposit.py | 34 + .../exchange/query/30_SubaccountTradeNonce.py | 36 + .../exchange/query/31_Positions.py | 19 + .../exchange/query/32_SubaccountPositions.py | 36 + .../query/33_SubaccountPossitionInMarket.py | 37 + ...34_SubaccountEffectivePossitionInMarket.py | 37 + .../exchange/query/35_PerpetualMarketInfo.py | 21 + .../query/36_ExpiryFuturesMarketInfo.py | 21 + .../query/37_PerpetualMarketFunding.py | 21 + .../query/38_SubaccountOrderMetadata.py | 36 + .../exchange/query/39_TradeRewardPoints.py | 34 + .../exchange/query/3_ExchangeBalances.py | 18 + .../query/40_PendingTradeRewardPoints.py | 34 + .../query/41_FeeDiscountAccountInfo.py | 34 + .../exchange/query/42_FeeDiscountSchedule.py | 19 + .../exchange/query/43_BalanceMismatches.py | 19 + .../query/44_BalanceWithBalanceHolds.py | 19 + .../query/45_FeeDiscountTierStatistics.py | 19 + .../exchange/query/46_MitoVaultInfos.py | 19 + .../query/47_QueryMarketIDFromVault.py | 19 + .../query/48_HistoricalTradeRecords.py | 21 + .../exchange/query/49_IsOptedOutOfRewards.py | 34 + .../exchange/query/4_AggregateVolume.py | 36 + .../query/50_OptedOutOfRewardsAccounts.py | 19 + .../exchange/query/51_MarketVolatility.py | 30 + .../exchange/query/52_BinaryOptionsMarkets.py | 19 + .../53_TraderDerivativeConditionalOrders.py | 37 + .../54_MarketAtomicExecutionFeeMultiplier.py | 21 + .../exchange/query/5_AggregateVolumes.py | 34 + .../exchange/query/6_AggregateMarketVolume.py | 21 + .../query/7_AggregateMarketVolumes.py | 21 + .../exchange/query/8_DenomDecimal.py | 19 + .../exchange/query/9_DenomDecimals.py | 19 + pyinjective/async_client.py | 393 +++ .../chain/grpc/chain_grpc_exchange_api.py | 572 ++++ .../configurable_exchange_query_servicer.py | 325 +++ .../grpc/test_chain_grpc_exchange_api.py | 2466 +++++++++++++++++ 87 files changed, 5229 insertions(+) rename examples/chain_client/distribution/{10_SetWithdrawAddress.py => 1_SetWithdrawAddress.py} (100%) rename examples/chain_client/distribution/{11_WithdrawDelegatorReward.py => 2_WithdrawDelegatorReward.py} (100%) rename examples/chain_client/distribution/{12_WithdrawValidatorCommission.py => 3_WithdrawValidatorCommission.py} (100%) rename examples/chain_client/distribution/{13_FundCommunityPool.py => 4_FundCommunityPool.py} (100%) rename examples/chain_client/distribution/{ => query}/1_ValidatorDistributionInfo.py (100%) rename examples/chain_client/distribution/{ => query}/2_ValidatorOutstandingRewards.py (100%) rename examples/chain_client/distribution/{ => query}/3_ValidatorCommission.py (100%) rename examples/chain_client/distribution/{ => query}/4_ValidatorSlashes.py (100%) rename examples/chain_client/distribution/{ => query}/5_DelegationRewards.py (100%) rename examples/chain_client/distribution/{ => query}/6_DelegationTotalRewards.py (100%) rename examples/chain_client/distribution/{ => query}/7_DelegatorValidators.py (100%) rename examples/chain_client/distribution/{ => query}/8_DelegatorWithdrawAddress.py (100%) rename examples/chain_client/distribution/{ => query}/9_CommunityPool.py (100%) rename examples/chain_client/{16_MsgSubaccountTransfer.py => exchange/10_MsgSubaccountTransfer.py} (100%) rename examples/chain_client/{17_MsgBatchUpdateOrders.py => exchange/11_MsgBatchUpdateOrders.py} (100%) rename examples/chain_client/{24_MsgRewardsOptOut.py => exchange/12_MsgRewardsOptOut.py} (100%) rename examples/chain_client/{30_ExternalTransfer.py => exchange/15_ExternalTransfer.py} (100%) rename examples/chain_client/{31_MsgCreateBinaryOptionsLimitOrder.py => exchange/16_MsgCreateBinaryOptionsLimitOrder.py} (100%) rename examples/chain_client/{32_MsgCreateBinaryOptionsMarketOrder.py => exchange/17_MsgCreateBinaryOptionsMarketOrder.py} (100%) rename examples/chain_client/{33_MsgCancelBinaryOptionsOrder.py => exchange/18_MsgCancelBinaryOptionsOrder.py} (100%) rename examples/chain_client/{77_MsgLiquidatePosition.py => exchange/19_MsgLiquidatePosition.py} (100%) rename examples/chain_client/{2_MsgDeposit.py => exchange/1_MsgDeposit.py} (100%) rename examples/chain_client/{ => exchange}/3_MsgCreateSpotLimitOrder.py (100%) rename examples/chain_client/{ => exchange}/4_MsgCreateSpotMarketOrder.py (100%) rename examples/chain_client/{ => exchange}/5_MsgCancelSpotOrder.py (100%) rename examples/chain_client/{ => exchange}/6_MsgCreateDerivativeLimitOrder.py (100%) rename examples/chain_client/{ => exchange}/7_MsgCreateDerivativeMarketOrder.py (100%) rename examples/chain_client/{ => exchange}/8_MsgCancelDerivativeOrder.py (100%) rename examples/chain_client/{15_MsgWithdraw.py => exchange/9_MsgWithdraw.py} (100%) create mode 100644 examples/chain_client/exchange/query/10_SpotMarkets.py create mode 100644 examples/chain_client/exchange/query/11_SpotMarket.py create mode 100644 examples/chain_client/exchange/query/12_FullSpotMarkets.py create mode 100644 examples/chain_client/exchange/query/13_FullSpotMarket.py create mode 100644 examples/chain_client/exchange/query/14_SpotOrderbook.py create mode 100644 examples/chain_client/exchange/query/15_TraderSpotOrders.py create mode 100644 examples/chain_client/exchange/query/16_AccountAddressSpotOrders.py create mode 100644 examples/chain_client/exchange/query/17_SpotOrdersByHashes.py create mode 100644 examples/chain_client/exchange/query/18_SubaccountOrders.py create mode 100644 examples/chain_client/exchange/query/19_TraderSpotTransientOrders.py create mode 100644 examples/chain_client/exchange/query/1_SubaccountDeposits.py create mode 100644 examples/chain_client/exchange/query/20_SpotMidPriceAndTOB.py create mode 100644 examples/chain_client/exchange/query/21_DerivativeMidPriceAndTOB.py create mode 100644 examples/chain_client/exchange/query/22_DerivativeOrderbook.py create mode 100644 examples/chain_client/exchange/query/23_TraderDerivativeOrders.py create mode 100644 examples/chain_client/exchange/query/24_AccountAddressDerivativeOrders.py create mode 100644 examples/chain_client/exchange/query/25_DerivativeOrdersByHashes.py create mode 100644 examples/chain_client/exchange/query/26_TraderDerivativeTransientOrders.py create mode 100644 examples/chain_client/exchange/query/27_DerivativeMarkets.py create mode 100644 examples/chain_client/exchange/query/28_DerivativeMarket.py create mode 100644 examples/chain_client/exchange/query/29_DerivativeMarketAddress.py create mode 100644 examples/chain_client/exchange/query/2_SubaccountDeposit.py create mode 100644 examples/chain_client/exchange/query/30_SubaccountTradeNonce.py create mode 100644 examples/chain_client/exchange/query/31_Positions.py create mode 100644 examples/chain_client/exchange/query/32_SubaccountPositions.py create mode 100644 examples/chain_client/exchange/query/33_SubaccountPossitionInMarket.py create mode 100644 examples/chain_client/exchange/query/34_SubaccountEffectivePossitionInMarket.py create mode 100644 examples/chain_client/exchange/query/35_PerpetualMarketInfo.py create mode 100644 examples/chain_client/exchange/query/36_ExpiryFuturesMarketInfo.py create mode 100644 examples/chain_client/exchange/query/37_PerpetualMarketFunding.py create mode 100644 examples/chain_client/exchange/query/38_SubaccountOrderMetadata.py create mode 100644 examples/chain_client/exchange/query/39_TradeRewardPoints.py create mode 100644 examples/chain_client/exchange/query/3_ExchangeBalances.py create mode 100644 examples/chain_client/exchange/query/40_PendingTradeRewardPoints.py create mode 100644 examples/chain_client/exchange/query/41_FeeDiscountAccountInfo.py create mode 100644 examples/chain_client/exchange/query/42_FeeDiscountSchedule.py create mode 100644 examples/chain_client/exchange/query/43_BalanceMismatches.py create mode 100644 examples/chain_client/exchange/query/44_BalanceWithBalanceHolds.py create mode 100644 examples/chain_client/exchange/query/45_FeeDiscountTierStatistics.py create mode 100644 examples/chain_client/exchange/query/46_MitoVaultInfos.py create mode 100644 examples/chain_client/exchange/query/47_QueryMarketIDFromVault.py create mode 100644 examples/chain_client/exchange/query/48_HistoricalTradeRecords.py create mode 100644 examples/chain_client/exchange/query/49_IsOptedOutOfRewards.py create mode 100644 examples/chain_client/exchange/query/4_AggregateVolume.py create mode 100644 examples/chain_client/exchange/query/50_OptedOutOfRewardsAccounts.py create mode 100644 examples/chain_client/exchange/query/51_MarketVolatility.py create mode 100644 examples/chain_client/exchange/query/52_BinaryOptionsMarkets.py create mode 100644 examples/chain_client/exchange/query/53_TraderDerivativeConditionalOrders.py create mode 100644 examples/chain_client/exchange/query/54_MarketAtomicExecutionFeeMultiplier.py create mode 100644 examples/chain_client/exchange/query/5_AggregateVolumes.py create mode 100644 examples/chain_client/exchange/query/6_AggregateMarketVolume.py create mode 100644 examples/chain_client/exchange/query/7_AggregateMarketVolumes.py create mode 100644 examples/chain_client/exchange/query/8_DenomDecimal.py create mode 100644 examples/chain_client/exchange/query/9_DenomDecimals.py create mode 100644 pyinjective/client/chain/grpc/chain_grpc_exchange_api.py create mode 100644 tests/client/chain/grpc/configurable_exchange_query_servicer.py create mode 100644 tests/client/chain/grpc/test_chain_grpc_exchange_api.py diff --git a/examples/chain_client/distribution/10_SetWithdrawAddress.py b/examples/chain_client/distribution/1_SetWithdrawAddress.py similarity index 100% rename from examples/chain_client/distribution/10_SetWithdrawAddress.py rename to examples/chain_client/distribution/1_SetWithdrawAddress.py diff --git a/examples/chain_client/distribution/11_WithdrawDelegatorReward.py b/examples/chain_client/distribution/2_WithdrawDelegatorReward.py similarity index 100% rename from examples/chain_client/distribution/11_WithdrawDelegatorReward.py rename to examples/chain_client/distribution/2_WithdrawDelegatorReward.py diff --git a/examples/chain_client/distribution/12_WithdrawValidatorCommission.py b/examples/chain_client/distribution/3_WithdrawValidatorCommission.py similarity index 100% rename from examples/chain_client/distribution/12_WithdrawValidatorCommission.py rename to examples/chain_client/distribution/3_WithdrawValidatorCommission.py diff --git a/examples/chain_client/distribution/13_FundCommunityPool.py b/examples/chain_client/distribution/4_FundCommunityPool.py similarity index 100% rename from examples/chain_client/distribution/13_FundCommunityPool.py rename to examples/chain_client/distribution/4_FundCommunityPool.py diff --git a/examples/chain_client/distribution/1_ValidatorDistributionInfo.py b/examples/chain_client/distribution/query/1_ValidatorDistributionInfo.py similarity index 100% rename from examples/chain_client/distribution/1_ValidatorDistributionInfo.py rename to examples/chain_client/distribution/query/1_ValidatorDistributionInfo.py diff --git a/examples/chain_client/distribution/2_ValidatorOutstandingRewards.py b/examples/chain_client/distribution/query/2_ValidatorOutstandingRewards.py similarity index 100% rename from examples/chain_client/distribution/2_ValidatorOutstandingRewards.py rename to examples/chain_client/distribution/query/2_ValidatorOutstandingRewards.py diff --git a/examples/chain_client/distribution/3_ValidatorCommission.py b/examples/chain_client/distribution/query/3_ValidatorCommission.py similarity index 100% rename from examples/chain_client/distribution/3_ValidatorCommission.py rename to examples/chain_client/distribution/query/3_ValidatorCommission.py diff --git a/examples/chain_client/distribution/4_ValidatorSlashes.py b/examples/chain_client/distribution/query/4_ValidatorSlashes.py similarity index 100% rename from examples/chain_client/distribution/4_ValidatorSlashes.py rename to examples/chain_client/distribution/query/4_ValidatorSlashes.py diff --git a/examples/chain_client/distribution/5_DelegationRewards.py b/examples/chain_client/distribution/query/5_DelegationRewards.py similarity index 100% rename from examples/chain_client/distribution/5_DelegationRewards.py rename to examples/chain_client/distribution/query/5_DelegationRewards.py diff --git a/examples/chain_client/distribution/6_DelegationTotalRewards.py b/examples/chain_client/distribution/query/6_DelegationTotalRewards.py similarity index 100% rename from examples/chain_client/distribution/6_DelegationTotalRewards.py rename to examples/chain_client/distribution/query/6_DelegationTotalRewards.py diff --git a/examples/chain_client/distribution/7_DelegatorValidators.py b/examples/chain_client/distribution/query/7_DelegatorValidators.py similarity index 100% rename from examples/chain_client/distribution/7_DelegatorValidators.py rename to examples/chain_client/distribution/query/7_DelegatorValidators.py diff --git a/examples/chain_client/distribution/8_DelegatorWithdrawAddress.py b/examples/chain_client/distribution/query/8_DelegatorWithdrawAddress.py similarity index 100% rename from examples/chain_client/distribution/8_DelegatorWithdrawAddress.py rename to examples/chain_client/distribution/query/8_DelegatorWithdrawAddress.py diff --git a/examples/chain_client/distribution/9_CommunityPool.py b/examples/chain_client/distribution/query/9_CommunityPool.py similarity index 100% rename from examples/chain_client/distribution/9_CommunityPool.py rename to examples/chain_client/distribution/query/9_CommunityPool.py diff --git a/examples/chain_client/16_MsgSubaccountTransfer.py b/examples/chain_client/exchange/10_MsgSubaccountTransfer.py similarity index 100% rename from examples/chain_client/16_MsgSubaccountTransfer.py rename to examples/chain_client/exchange/10_MsgSubaccountTransfer.py diff --git a/examples/chain_client/17_MsgBatchUpdateOrders.py b/examples/chain_client/exchange/11_MsgBatchUpdateOrders.py similarity index 100% rename from examples/chain_client/17_MsgBatchUpdateOrders.py rename to examples/chain_client/exchange/11_MsgBatchUpdateOrders.py diff --git a/examples/chain_client/24_MsgRewardsOptOut.py b/examples/chain_client/exchange/12_MsgRewardsOptOut.py similarity index 100% rename from examples/chain_client/24_MsgRewardsOptOut.py rename to examples/chain_client/exchange/12_MsgRewardsOptOut.py diff --git a/examples/chain_client/30_ExternalTransfer.py b/examples/chain_client/exchange/15_ExternalTransfer.py similarity index 100% rename from examples/chain_client/30_ExternalTransfer.py rename to examples/chain_client/exchange/15_ExternalTransfer.py diff --git a/examples/chain_client/31_MsgCreateBinaryOptionsLimitOrder.py b/examples/chain_client/exchange/16_MsgCreateBinaryOptionsLimitOrder.py similarity index 100% rename from examples/chain_client/31_MsgCreateBinaryOptionsLimitOrder.py rename to examples/chain_client/exchange/16_MsgCreateBinaryOptionsLimitOrder.py diff --git a/examples/chain_client/32_MsgCreateBinaryOptionsMarketOrder.py b/examples/chain_client/exchange/17_MsgCreateBinaryOptionsMarketOrder.py similarity index 100% rename from examples/chain_client/32_MsgCreateBinaryOptionsMarketOrder.py rename to examples/chain_client/exchange/17_MsgCreateBinaryOptionsMarketOrder.py diff --git a/examples/chain_client/33_MsgCancelBinaryOptionsOrder.py b/examples/chain_client/exchange/18_MsgCancelBinaryOptionsOrder.py similarity index 100% rename from examples/chain_client/33_MsgCancelBinaryOptionsOrder.py rename to examples/chain_client/exchange/18_MsgCancelBinaryOptionsOrder.py diff --git a/examples/chain_client/77_MsgLiquidatePosition.py b/examples/chain_client/exchange/19_MsgLiquidatePosition.py similarity index 100% rename from examples/chain_client/77_MsgLiquidatePosition.py rename to examples/chain_client/exchange/19_MsgLiquidatePosition.py diff --git a/examples/chain_client/2_MsgDeposit.py b/examples/chain_client/exchange/1_MsgDeposit.py similarity index 100% rename from examples/chain_client/2_MsgDeposit.py rename to examples/chain_client/exchange/1_MsgDeposit.py diff --git a/examples/chain_client/3_MsgCreateSpotLimitOrder.py b/examples/chain_client/exchange/3_MsgCreateSpotLimitOrder.py similarity index 100% rename from examples/chain_client/3_MsgCreateSpotLimitOrder.py rename to examples/chain_client/exchange/3_MsgCreateSpotLimitOrder.py diff --git a/examples/chain_client/4_MsgCreateSpotMarketOrder.py b/examples/chain_client/exchange/4_MsgCreateSpotMarketOrder.py similarity index 100% rename from examples/chain_client/4_MsgCreateSpotMarketOrder.py rename to examples/chain_client/exchange/4_MsgCreateSpotMarketOrder.py diff --git a/examples/chain_client/5_MsgCancelSpotOrder.py b/examples/chain_client/exchange/5_MsgCancelSpotOrder.py similarity index 100% rename from examples/chain_client/5_MsgCancelSpotOrder.py rename to examples/chain_client/exchange/5_MsgCancelSpotOrder.py diff --git a/examples/chain_client/6_MsgCreateDerivativeLimitOrder.py b/examples/chain_client/exchange/6_MsgCreateDerivativeLimitOrder.py similarity index 100% rename from examples/chain_client/6_MsgCreateDerivativeLimitOrder.py rename to examples/chain_client/exchange/6_MsgCreateDerivativeLimitOrder.py diff --git a/examples/chain_client/7_MsgCreateDerivativeMarketOrder.py b/examples/chain_client/exchange/7_MsgCreateDerivativeMarketOrder.py similarity index 100% rename from examples/chain_client/7_MsgCreateDerivativeMarketOrder.py rename to examples/chain_client/exchange/7_MsgCreateDerivativeMarketOrder.py diff --git a/examples/chain_client/8_MsgCancelDerivativeOrder.py b/examples/chain_client/exchange/8_MsgCancelDerivativeOrder.py similarity index 100% rename from examples/chain_client/8_MsgCancelDerivativeOrder.py rename to examples/chain_client/exchange/8_MsgCancelDerivativeOrder.py diff --git a/examples/chain_client/15_MsgWithdraw.py b/examples/chain_client/exchange/9_MsgWithdraw.py similarity index 100% rename from examples/chain_client/15_MsgWithdraw.py rename to examples/chain_client/exchange/9_MsgWithdraw.py diff --git a/examples/chain_client/exchange/query/10_SpotMarkets.py b/examples/chain_client/exchange/query/10_SpotMarkets.py new file mode 100644 index 00000000..4cedc9d7 --- /dev/null +++ b/examples/chain_client/exchange/query/10_SpotMarkets.py @@ -0,0 +1,22 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + spot_markets = await client.fetch_chain_spot_markets( + status="Active", + market_ids=["0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"], + ) + print(spot_markets) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/11_SpotMarket.py b/examples/chain_client/exchange/query/11_SpotMarket.py new file mode 100644 index 00000000..0e774edf --- /dev/null +++ b/examples/chain_client/exchange/query/11_SpotMarket.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + spot_market = await client.fetch_chain_spot_market( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + ) + print(spot_market) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/12_FullSpotMarkets.py b/examples/chain_client/exchange/query/12_FullSpotMarkets.py new file mode 100644 index 00000000..cfefee28 --- /dev/null +++ b/examples/chain_client/exchange/query/12_FullSpotMarkets.py @@ -0,0 +1,23 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + spot_markets = await client.fetch_chain_full_spot_markets( + status="Active", + market_ids=["0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"], + with_mid_price_and_tob=True, + ) + print(spot_markets) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/13_FullSpotMarket.py b/examples/chain_client/exchange/query/13_FullSpotMarket.py new file mode 100644 index 00000000..6a39269d --- /dev/null +++ b/examples/chain_client/exchange/query/13_FullSpotMarket.py @@ -0,0 +1,22 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + spot_market = await client.fetch_chain_full_spot_market( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + with_mid_price_and_tob=True, + ) + print(spot_market) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/14_SpotOrderbook.py b/examples/chain_client/exchange/query/14_SpotOrderbook.py new file mode 100644 index 00000000..7b5a9aa1 --- /dev/null +++ b/examples/chain_client/exchange/query/14_SpotOrderbook.py @@ -0,0 +1,26 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.client.model.pagination import PaginationOption +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + pagination = PaginationOption(limit=2) + + orderbook = await client.fetch_chain_spot_orderbook( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + order_side="Buy", + pagination=pagination, + ) + print(orderbook) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/15_TraderSpotOrders.py b/examples/chain_client/exchange/query/15_TraderSpotOrders.py new file mode 100644 index 00000000..0cc96b6f --- /dev/null +++ b/examples/chain_client/exchange/query/15_TraderSpotOrders.py @@ -0,0 +1,37 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + orders = await client.fetch_chain_trader_spot_orders( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + subaccount_id=subaccount_id, + ) + print(orders) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/16_AccountAddressSpotOrders.py b/examples/chain_client/exchange/query/16_AccountAddressSpotOrders.py new file mode 100644 index 00000000..8e50fa95 --- /dev/null +++ b/examples/chain_client/exchange/query/16_AccountAddressSpotOrders.py @@ -0,0 +1,35 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + orders = await client.fetch_chain_account_address_spot_orders( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + account_address=address.to_acc_bech32(), + ) + print(orders) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/17_SpotOrdersByHashes.py b/examples/chain_client/exchange/query/17_SpotOrdersByHashes.py new file mode 100644 index 00000000..641eb6f5 --- /dev/null +++ b/examples/chain_client/exchange/query/17_SpotOrdersByHashes.py @@ -0,0 +1,38 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + orders = await client.fetch_chain_spot_orders_by_hashes( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + subaccount_id=subaccount_id, + order_hashes=["0x57a01cd26f1e2080860af3264e865d7c9c034a701e30946d01c1dc7a303cf2c1"], + ) + print(orders) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/18_SubaccountOrders.py b/examples/chain_client/exchange/query/18_SubaccountOrders.py new file mode 100644 index 00000000..4983f827 --- /dev/null +++ b/examples/chain_client/exchange/query/18_SubaccountOrders.py @@ -0,0 +1,37 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + orders = await client.fetch_chain_subaccount_orders( + subaccount_id=subaccount_id, + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + ) + print(orders) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/19_TraderSpotTransientOrders.py b/examples/chain_client/exchange/query/19_TraderSpotTransientOrders.py new file mode 100644 index 00000000..2b29c2c0 --- /dev/null +++ b/examples/chain_client/exchange/query/19_TraderSpotTransientOrders.py @@ -0,0 +1,37 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + orders = await client.fetch_chain_trader_spot_transient_orders( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + subaccount_id=subaccount_id, + ) + print(orders) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/1_SubaccountDeposits.py b/examples/chain_client/exchange/query/1_SubaccountDeposits.py new file mode 100644 index 00000000..f9afb8ae --- /dev/null +++ b/examples/chain_client/exchange/query/1_SubaccountDeposits.py @@ -0,0 +1,34 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + deposits = await client.fetch_subaccount_deposits(subaccount_id=subaccount_id) + print(deposits) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/20_SpotMidPriceAndTOB.py b/examples/chain_client/exchange/query/20_SpotMidPriceAndTOB.py new file mode 100644 index 00000000..35493ffd --- /dev/null +++ b/examples/chain_client/exchange/query/20_SpotMidPriceAndTOB.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + prices = await client.fetch_spot_mid_price_and_tob( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + ) + print(prices) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/21_DerivativeMidPriceAndTOB.py b/examples/chain_client/exchange/query/21_DerivativeMidPriceAndTOB.py new file mode 100644 index 00000000..5b5c5fff --- /dev/null +++ b/examples/chain_client/exchange/query/21_DerivativeMidPriceAndTOB.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + prices = await client.fetch_derivative_mid_price_and_tob( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + print(prices) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/22_DerivativeOrderbook.py b/examples/chain_client/exchange/query/22_DerivativeOrderbook.py new file mode 100644 index 00000000..465a62b6 --- /dev/null +++ b/examples/chain_client/exchange/query/22_DerivativeOrderbook.py @@ -0,0 +1,25 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.client.model.pagination import PaginationOption +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + pagination = PaginationOption(limit=2) + + orderbook = await client.fetch_chain_derivative_orderbook( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + pagination=pagination, + ) + print(orderbook) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/23_TraderDerivativeOrders.py b/examples/chain_client/exchange/query/23_TraderDerivativeOrders.py new file mode 100644 index 00000000..61e98ba1 --- /dev/null +++ b/examples/chain_client/exchange/query/23_TraderDerivativeOrders.py @@ -0,0 +1,37 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + orders = await client.fetch_chain_trader_spot_orders( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + subaccount_id=subaccount_id, + ) + print(orders) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/24_AccountAddressDerivativeOrders.py b/examples/chain_client/exchange/query/24_AccountAddressDerivativeOrders.py new file mode 100644 index 00000000..469c99fb --- /dev/null +++ b/examples/chain_client/exchange/query/24_AccountAddressDerivativeOrders.py @@ -0,0 +1,35 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + orders = await client.fetch_chain_account_address_spot_orders( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + account_address=address.to_acc_bech32(), + ) + print(orders) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/25_DerivativeOrdersByHashes.py b/examples/chain_client/exchange/query/25_DerivativeOrdersByHashes.py new file mode 100644 index 00000000..ea700e6d --- /dev/null +++ b/examples/chain_client/exchange/query/25_DerivativeOrdersByHashes.py @@ -0,0 +1,38 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + orders = await client.fetch_chain_spot_orders_by_hashes( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + subaccount_id=subaccount_id, + order_hashes=["0x57a01cd26f1e2080860af3264e865d7c9c034a701e30946d01c1dc7a303cf2c1"], + ) + print(orders) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/26_TraderDerivativeTransientOrders.py b/examples/chain_client/exchange/query/26_TraderDerivativeTransientOrders.py new file mode 100644 index 00000000..c5548d59 --- /dev/null +++ b/examples/chain_client/exchange/query/26_TraderDerivativeTransientOrders.py @@ -0,0 +1,37 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + orders = await client.fetch_chain_trader_derivative_transient_orders( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + subaccount_id=subaccount_id, + ) + print(orders) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/27_DerivativeMarkets.py b/examples/chain_client/exchange/query/27_DerivativeMarkets.py new file mode 100644 index 00000000..2f4bbc5a --- /dev/null +++ b/examples/chain_client/exchange/query/27_DerivativeMarkets.py @@ -0,0 +1,22 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + derivative_markets = await client.fetch_chain_derivative_markets( + status="Active", + market_ids=["0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"], + ) + print(derivative_markets) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/28_DerivativeMarket.py b/examples/chain_client/exchange/query/28_DerivativeMarket.py new file mode 100644 index 00000000..152381f5 --- /dev/null +++ b/examples/chain_client/exchange/query/28_DerivativeMarket.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + derivative_market = await client.fetch_chain_derivative_market( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + print(derivative_market) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/29_DerivativeMarketAddress.py b/examples/chain_client/exchange/query/29_DerivativeMarketAddress.py new file mode 100644 index 00000000..c2d88805 --- /dev/null +++ b/examples/chain_client/exchange/query/29_DerivativeMarketAddress.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + address = await client.fetch_derivative_market_address( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + print(address) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/2_SubaccountDeposit.py b/examples/chain_client/exchange/query/2_SubaccountDeposit.py new file mode 100644 index 00000000..5002397d --- /dev/null +++ b/examples/chain_client/exchange/query/2_SubaccountDeposit.py @@ -0,0 +1,34 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + deposit = await client.fetch_subaccount_deposit(subaccount_id=subaccount_id, denom="inj") + print(deposit) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/30_SubaccountTradeNonce.py b/examples/chain_client/exchange/query/30_SubaccountTradeNonce.py new file mode 100644 index 00000000..ad81aca3 --- /dev/null +++ b/examples/chain_client/exchange/query/30_SubaccountTradeNonce.py @@ -0,0 +1,36 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + nonce = await client.fetch_subaccount_trade_nonce( + subaccount_id=subaccount_id, + ) + print(nonce) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/31_Positions.py b/examples/chain_client/exchange/query/31_Positions.py new file mode 100644 index 00000000..ae494b6a --- /dev/null +++ b/examples/chain_client/exchange/query/31_Positions.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + positions = await client.fetch_chain_positions() + print(positions) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/32_SubaccountPositions.py b/examples/chain_client/exchange/query/32_SubaccountPositions.py new file mode 100644 index 00000000..000d95b6 --- /dev/null +++ b/examples/chain_client/exchange/query/32_SubaccountPositions.py @@ -0,0 +1,36 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + positions = await client.fetch_chain_subaccount_positions( + subaccount_id=subaccount_id, + ) + print(positions) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/33_SubaccountPossitionInMarket.py b/examples/chain_client/exchange/query/33_SubaccountPossitionInMarket.py new file mode 100644 index 00000000..4e0f1ddb --- /dev/null +++ b/examples/chain_client/exchange/query/33_SubaccountPossitionInMarket.py @@ -0,0 +1,37 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + position = await client.fetch_chain_subaccount_position_in_market( + subaccount_id=subaccount_id, + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + print(position) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/34_SubaccountEffectivePossitionInMarket.py b/examples/chain_client/exchange/query/34_SubaccountEffectivePossitionInMarket.py new file mode 100644 index 00000000..e729e77c --- /dev/null +++ b/examples/chain_client/exchange/query/34_SubaccountEffectivePossitionInMarket.py @@ -0,0 +1,37 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + position = await client.fetch_chain_subaccount_effective_position_in_market( + subaccount_id=subaccount_id, + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + print(position) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/35_PerpetualMarketInfo.py b/examples/chain_client/exchange/query/35_PerpetualMarketInfo.py new file mode 100644 index 00000000..ca27f552 --- /dev/null +++ b/examples/chain_client/exchange/query/35_PerpetualMarketInfo.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + market_info = await client.fetch_chain_perpetual_market_info( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + print(market_info) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/36_ExpiryFuturesMarketInfo.py b/examples/chain_client/exchange/query/36_ExpiryFuturesMarketInfo.py new file mode 100644 index 00000000..4053013c --- /dev/null +++ b/examples/chain_client/exchange/query/36_ExpiryFuturesMarketInfo.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + market_info = await client.fetch_chain_expiry_futures_market_info( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + print(market_info) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/37_PerpetualMarketFunding.py b/examples/chain_client/exchange/query/37_PerpetualMarketFunding.py new file mode 100644 index 00000000..099c2a0f --- /dev/null +++ b/examples/chain_client/exchange/query/37_PerpetualMarketFunding.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + funding = await client.fetch_chain_perpetual_market_funding( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + print(funding) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/38_SubaccountOrderMetadata.py b/examples/chain_client/exchange/query/38_SubaccountOrderMetadata.py new file mode 100644 index 00000000..f4af9d38 --- /dev/null +++ b/examples/chain_client/exchange/query/38_SubaccountOrderMetadata.py @@ -0,0 +1,36 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + metadata = await client.fetch_subaccount_order_metadata( + subaccount_id=subaccount_id, + ) + print(metadata) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/39_TradeRewardPoints.py b/examples/chain_client/exchange/query/39_TradeRewardPoints.py new file mode 100644 index 00000000..13f08730 --- /dev/null +++ b/examples/chain_client/exchange/query/39_TradeRewardPoints.py @@ -0,0 +1,34 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + points = await client.fetch_trade_reward_points( + accounts=[address.to_acc_bech32()], + ) + print(points) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/3_ExchangeBalances.py b/examples/chain_client/exchange/query/3_ExchangeBalances.py new file mode 100644 index 00000000..091bf10c --- /dev/null +++ b/examples/chain_client/exchange/query/3_ExchangeBalances.py @@ -0,0 +1,18 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + # initialize grpc client + client = AsyncClient(network) + + balances = await client.fetch_exchange_balances() + print(balances) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/40_PendingTradeRewardPoints.py b/examples/chain_client/exchange/query/40_PendingTradeRewardPoints.py new file mode 100644 index 00000000..fa3e8aa2 --- /dev/null +++ b/examples/chain_client/exchange/query/40_PendingTradeRewardPoints.py @@ -0,0 +1,34 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + points = await client.fetch_pending_trade_reward_points( + accounts=[address.to_acc_bech32()], + ) + print(points) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/41_FeeDiscountAccountInfo.py b/examples/chain_client/exchange/query/41_FeeDiscountAccountInfo.py new file mode 100644 index 00000000..15fbb7ce --- /dev/null +++ b/examples/chain_client/exchange/query/41_FeeDiscountAccountInfo.py @@ -0,0 +1,34 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + fee_discount = await client.fetch_fee_discount_account_info( + account=address.to_acc_bech32(), + ) + print(fee_discount) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/42_FeeDiscountSchedule.py b/examples/chain_client/exchange/query/42_FeeDiscountSchedule.py new file mode 100644 index 00000000..a0ae96cb --- /dev/null +++ b/examples/chain_client/exchange/query/42_FeeDiscountSchedule.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + schedule = await client.fetch_fee_discount_schedule() + print(schedule) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/43_BalanceMismatches.py b/examples/chain_client/exchange/query/43_BalanceMismatches.py new file mode 100644 index 00000000..c7f7ca5e --- /dev/null +++ b/examples/chain_client/exchange/query/43_BalanceMismatches.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + mismatches = await client.fetch_balance_mismatches(dust_factor=1) + print(mismatches) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/44_BalanceWithBalanceHolds.py b/examples/chain_client/exchange/query/44_BalanceWithBalanceHolds.py new file mode 100644 index 00000000..6587c59a --- /dev/null +++ b/examples/chain_client/exchange/query/44_BalanceWithBalanceHolds.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + balance = await client.fetch_balance_with_balance_holds() + print(balance) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/45_FeeDiscountTierStatistics.py b/examples/chain_client/exchange/query/45_FeeDiscountTierStatistics.py new file mode 100644 index 00000000..5671e4ce --- /dev/null +++ b/examples/chain_client/exchange/query/45_FeeDiscountTierStatistics.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + statistics = await client.fetch_fee_discount_tier_statistics() + print(statistics) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/46_MitoVaultInfos.py b/examples/chain_client/exchange/query/46_MitoVaultInfos.py new file mode 100644 index 00000000..3faa5cb9 --- /dev/null +++ b/examples/chain_client/exchange/query/46_MitoVaultInfos.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + infos = await client.fetch_mito_vault_infos() + print(infos) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/47_QueryMarketIDFromVault.py b/examples/chain_client/exchange/query/47_QueryMarketIDFromVault.py new file mode 100644 index 00000000..e699dbaa --- /dev/null +++ b/examples/chain_client/exchange/query/47_QueryMarketIDFromVault.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + market_id = await client.fetch_market_id_from_vault(vault_address="inj1qg5ega6dykkxc307y25pecuufrjkxkag6xhp6y") + print(market_id) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/48_HistoricalTradeRecords.py b/examples/chain_client/exchange/query/48_HistoricalTradeRecords.py new file mode 100644 index 00000000..6b93200f --- /dev/null +++ b/examples/chain_client/exchange/query/48_HistoricalTradeRecords.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + records = await client.fetch_historical_trade_records( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe" + ) + print(records) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/49_IsOptedOutOfRewards.py b/examples/chain_client/exchange/query/49_IsOptedOutOfRewards.py new file mode 100644 index 00000000..28c0925c --- /dev/null +++ b/examples/chain_client/exchange/query/49_IsOptedOutOfRewards.py @@ -0,0 +1,34 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + is_opted_out = await client.fetch_is_opted_out_of_rewards( + account=address.to_acc_bech32(), + ) + print(is_opted_out) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/4_AggregateVolume.py b/examples/chain_client/exchange/query/4_AggregateVolume.py new file mode 100644 index 00000000..35c334a5 --- /dev/null +++ b/examples/chain_client/exchange/query/4_AggregateVolume.py @@ -0,0 +1,36 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + + subaccount_id = address.get_subaccount_id(index=0) + + volume = await client.fetch_aggregate_volume(account=address.to_acc_bech32()) + print(volume) + + volume = await client.fetch_aggregate_volume(account=subaccount_id) + print(volume) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/50_OptedOutOfRewardsAccounts.py b/examples/chain_client/exchange/query/50_OptedOutOfRewardsAccounts.py new file mode 100644 index 00000000..9940f799 --- /dev/null +++ b/examples/chain_client/exchange/query/50_OptedOutOfRewardsAccounts.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + opted_out = await client.fetch_opted_out_of_rewards_accounts() + print(opted_out) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/51_MarketVolatility.py b/examples/chain_client/exchange/query/51_MarketVolatility.py new file mode 100644 index 00000000..3173ca39 --- /dev/null +++ b/examples/chain_client/exchange/query/51_MarketVolatility.py @@ -0,0 +1,30 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + market_id = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe" + trade_grouping_sec = 10 + max_age = 0 + include_raw_history = True + include_metadata = True + volatility = await client.fetch_market_volatility( + market_id=market_id, + trade_grouping_sec=trade_grouping_sec, + max_age=max_age, + include_raw_history=include_raw_history, + include_metadata=include_metadata, + ) + print(volatility) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/52_BinaryOptionsMarkets.py b/examples/chain_client/exchange/query/52_BinaryOptionsMarkets.py new file mode 100644 index 00000000..4448a4ec --- /dev/null +++ b/examples/chain_client/exchange/query/52_BinaryOptionsMarkets.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + markets = await client.fetch_chain_binary_options_markets(status="Active") + print(markets) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/53_TraderDerivativeConditionalOrders.py b/examples/chain_client/exchange/query/53_TraderDerivativeConditionalOrders.py new file mode 100644 index 00000000..fd65e68f --- /dev/null +++ b/examples/chain_client/exchange/query/53_TraderDerivativeConditionalOrders.py @@ -0,0 +1,37 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + await client.fetch_account(address.to_acc_bech32()) + + subaccount_id = address.get_subaccount_id(index=0) + + orders = await client.fetch_trader_derivative_conditional_orders( + subaccount_id=subaccount_id, + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + print(orders) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/54_MarketAtomicExecutionFeeMultiplier.py b/examples/chain_client/exchange/query/54_MarketAtomicExecutionFeeMultiplier.py new file mode 100644 index 00000000..328028d3 --- /dev/null +++ b/examples/chain_client/exchange/query/54_MarketAtomicExecutionFeeMultiplier.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + multiplier = await client.fetch_market_atomic_execution_fee_multiplier( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + print(multiplier) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/5_AggregateVolumes.py b/examples/chain_client/exchange/query/5_AggregateVolumes.py new file mode 100644 index 00000000..6effe4be --- /dev/null +++ b/examples/chain_client/exchange/query/5_AggregateVolumes.py @@ -0,0 +1,34 @@ +import asyncio +import os + +import dotenv + +from pyinjective import PrivateKey +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + # load account + priv_key = PrivateKey.from_hex(configured_private_key) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + + volume = await client.fetch_aggregate_volumes( + accounts=[address.to_acc_bech32()], + market_ids=["0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"], + ) + print(volume) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/6_AggregateMarketVolume.py b/examples/chain_client/exchange/query/6_AggregateMarketVolume.py new file mode 100644 index 00000000..b3262d82 --- /dev/null +++ b/examples/chain_client/exchange/query/6_AggregateMarketVolume.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + market_id = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe" + + volume = await client.fetch_aggregate_market_volume(market_id=market_id) + print(volume) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/7_AggregateMarketVolumes.py b/examples/chain_client/exchange/query/7_AggregateMarketVolumes.py new file mode 100644 index 00000000..23dfa0ec --- /dev/null +++ b/examples/chain_client/exchange/query/7_AggregateMarketVolumes.py @@ -0,0 +1,21 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + volume = await client.fetch_aggregate_market_volumes( + market_ids=["0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"], + ) + print(volume) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/8_DenomDecimal.py b/examples/chain_client/exchange/query/8_DenomDecimal.py new file mode 100644 index 00000000..2079f5e8 --- /dev/null +++ b/examples/chain_client/exchange/query/8_DenomDecimal.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + deposits = await client.fetch_denom_decimal(denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5") + print(deposits) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/exchange/query/9_DenomDecimals.py b/examples/chain_client/exchange/query/9_DenomDecimals.py new file mode 100644 index 00000000..d96df30b --- /dev/null +++ b/examples/chain_client/exchange/query/9_DenomDecimals.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + + # initialize grpc client + client = AsyncClient(network) + + deposits = await client.fetch_denom_decimals(denoms=["inj", "peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"]) + print(deposits) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/pyinjective/async_client.py b/pyinjective/async_client.py index dea12f12..0af37da0 100644 --- a/pyinjective/async_client.py +++ b/pyinjective/async_client.py @@ -13,6 +13,7 @@ from pyinjective.client.chain.grpc.chain_grpc_authz_api import ChainGrpcAuthZApi from pyinjective.client.chain.grpc.chain_grpc_bank_api import ChainGrpcBankApi from pyinjective.client.chain.grpc.chain_grpc_distribution_api import ChainGrpcDistributionApi +from pyinjective.client.chain.grpc.chain_grpc_exchange_api import ChainGrpcExchangeApi from pyinjective.client.chain.grpc.chain_grpc_token_factory_api import ChainGrpcTokenFactoryApi from pyinjective.client.chain.grpc.chain_grpc_wasm_api import ChainGrpcWasmApi from pyinjective.client.chain.grpc_stream.chain_grpc_chain_stream import ChainGrpcChainStream @@ -185,6 +186,12 @@ def __init__( metadata_query_provider=self._chain_cookie_metadata_requestor ), ) + self.chain_exchange_api = ChainGrpcExchangeApi( + channel=self.chain_channel, + metadata_provider=lambda: self.network.chain_metadata( + metadata_query_provider=self._chain_cookie_metadata_requestor + ), + ) self.token_factory_api = ChainGrpcTokenFactoryApi( channel=self.chain_channel, metadata_provider=lambda: self.network.chain_metadata( @@ -639,6 +646,392 @@ async def fetch_delegator_withdraw_address( async def fetch_community_pool(self) -> Dict[str, Any]: return await self.distribution_api.fetch_community_pool() + # Exchange module + + async def fetch_subaccount_deposits( + self, + subaccount_id: Optional[str] = None, + subaccount_trader: Optional[str] = None, + subaccount_nonce: Optional[int] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_subaccount_deposits( + subaccount_id=subaccount_id, + subaccount_trader=subaccount_trader, + subaccount_nonce=subaccount_nonce, + ) + + async def fetch_subaccount_deposit( + self, + subaccount_id: str, + denom: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_subaccount_deposit( + subaccount_id=subaccount_id, + denom=denom, + ) + + async def fetch_exchange_balances(self) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_exchange_balances() + + async def fetch_aggregate_volume(self, account: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_aggregate_volume(account=account) + + async def fetch_aggregate_volumes( + self, + accounts: Optional[List[str]] = None, + market_ids: Optional[List[str]] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_aggregate_volumes( + accounts=accounts, + market_ids=market_ids, + ) + + async def fetch_aggregate_market_volume( + self, + market_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_aggregate_market_volume( + market_id=market_id, + ) + + async def fetch_aggregate_market_volumes( + self, + market_ids: Optional[List[str]] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_aggregate_market_volumes( + market_ids=market_ids, + ) + + async def fetch_denom_decimal(self, denom: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_denom_decimal(denom=denom) + + async def fetch_denom_decimals(self, denoms: Optional[List[str]] = None) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_denom_decimals(denoms=denoms) + + async def fetch_chain_spot_markets( + self, + status: Optional[str] = None, + market_ids: Optional[List[str]] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_spot_markets( + status=status, + market_ids=market_ids, + ) + + async def fetch_chain_spot_market( + self, + market_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_spot_market( + market_id=market_id, + ) + + async def fetch_chain_full_spot_markets( + self, + status: Optional[str] = None, + market_ids: Optional[List[str]] = None, + with_mid_price_and_tob: Optional[bool] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_full_spot_markets( + status=status, + market_ids=market_ids, + with_mid_price_and_tob=with_mid_price_and_tob, + ) + + async def fetch_chain_full_spot_market( + self, + market_id: str, + with_mid_price_and_tob: Optional[bool] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_full_spot_market( + market_id=market_id, + with_mid_price_and_tob=with_mid_price_and_tob, + ) + + async def fetch_chain_spot_orderbook( + self, + market_id: str, + order_side: Optional[str] = None, + limit_cumulative_notional: Optional[str] = None, + limit_cumulative_quantity: Optional[str] = None, + pagination: Optional[PaginationOption] = None, + ) -> Dict[str, Any]: + # Order side could be "Side_Unspecified", "Buy", "Sell" + return await self.chain_exchange_api.fetch_spot_orderbook( + market_id=market_id, + order_side=order_side, + limit_cumulative_notional=limit_cumulative_notional, + limit_cumulative_quantity=limit_cumulative_quantity, + pagination=pagination, + ) + + async def fetch_chain_trader_spot_orders( + self, + market_id: str, + subaccount_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_trader_spot_orders( + market_id=market_id, + subaccount_id=subaccount_id, + ) + + async def fetch_chain_account_address_spot_orders( + self, + market_id: str, + account_address: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_account_address_spot_orders( + market_id=market_id, + account_address=account_address, + ) + + async def fetch_chain_spot_orders_by_hashes( + self, + market_id: str, + subaccount_id: str, + order_hashes: List[str], + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_spot_orders_by_hashes( + market_id=market_id, + subaccount_id=subaccount_id, + order_hashes=order_hashes, + ) + + async def fetch_chain_subaccount_orders( + self, + subaccount_id: str, + market_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_subaccount_orders( + subaccount_id=subaccount_id, + market_id=market_id, + ) + + async def fetch_chain_trader_spot_transient_orders( + self, + market_id: str, + subaccount_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_trader_spot_transient_orders( + market_id=market_id, + subaccount_id=subaccount_id, + ) + + async def fetch_spot_mid_price_and_tob( + self, + market_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_spot_mid_price_and_tob( + market_id=market_id, + ) + + async def fetch_derivative_mid_price_and_tob( + self, + market_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_derivative_mid_price_and_tob( + market_id=market_id, + ) + + async def fetch_chain_derivative_orderbook( + self, + market_id: str, + limit_cumulative_notional: Optional[str] = None, + pagination: Optional[PaginationOption] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_derivative_orderbook( + market_id=market_id, + limit_cumulative_notional=limit_cumulative_notional, + pagination=pagination, + ) + + async def fetch_chain_trader_derivative_orders( + self, + market_id: str, + subaccount_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_trader_derivative_orders( + market_id=market_id, + subaccount_id=subaccount_id, + ) + + async def fetch_chain_account_address_derivative_orders( + self, + market_id: str, + account_address: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_account_address_derivative_orders( + market_id=market_id, + account_address=account_address, + ) + + async def fetch_chain_derivative_orders_by_hashes( + self, + market_id: str, + subaccount_id: str, + order_hashes: List[str], + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_derivative_orders_by_hashes( + market_id=market_id, + subaccount_id=subaccount_id, + order_hashes=order_hashes, + ) + + async def fetch_chain_trader_derivative_transient_orders( + self, + market_id: str, + subaccount_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_trader_derivative_transient_orders( + market_id=market_id, + subaccount_id=subaccount_id, + ) + + async def fetch_chain_derivative_markets( + self, + status: Optional[str] = None, + market_ids: Optional[List[str]] = None, + with_mid_price_and_tob: Optional[bool] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_derivative_markets( + status=status, + market_ids=market_ids, + with_mid_price_and_tob=with_mid_price_and_tob, + ) + + async def fetch_chain_derivative_market( + self, + market_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_derivative_market( + market_id=market_id, + ) + + async def fetch_derivative_market_address(self, market_id: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_derivative_market_address(market_id=market_id) + + async def fetch_subaccount_trade_nonce(self, subaccount_id: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_subaccount_trade_nonce(subaccount_id=subaccount_id) + + async def fetch_chain_positions(self) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_positions() + + async def fetch_chain_subaccount_positions(self, subaccount_id: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_subaccount_positions(subaccount_id=subaccount_id) + + async def fetch_chain_subaccount_position_in_market(self, subaccount_id: str, market_id: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_subaccount_position_in_market( + subaccount_id=subaccount_id, + market_id=market_id, + ) + + async def fetch_chain_subaccount_effective_position_in_market( + self, subaccount_id: str, market_id: str + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_subaccount_effective_position_in_market( + subaccount_id=subaccount_id, + market_id=market_id, + ) + + async def fetch_chain_perpetual_market_info(self, market_id: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_perpetual_market_info(market_id=market_id) + + async def fetch_chain_expiry_futures_market_info(self, market_id: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_expiry_futures_market_info(market_id=market_id) + + async def fetch_chain_perpetual_market_funding(self, market_id: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_perpetual_market_funding(market_id=market_id) + + async def fetch_subaccount_order_metadata(self, subaccount_id: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_subaccount_order_metadata(subaccount_id=subaccount_id) + + async def fetch_trade_reward_points( + self, + accounts: Optional[List[str]] = None, + pending_pool_timestamp: Optional[int] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_trade_reward_points( + accounts=accounts, + pending_pool_timestamp=pending_pool_timestamp, + ) + + async def fetch_pending_trade_reward_points( + self, + accounts: Optional[List[str]] = None, + pending_pool_timestamp: Optional[int] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_pending_trade_reward_points( + accounts=accounts, + pending_pool_timestamp=pending_pool_timestamp, + ) + + async def fetch_fee_discount_account_info(self, account: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_fee_discount_account_info(account=account) + + async def fetch_fee_discount_schedule(self) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_fee_discount_schedule() + + async def fetch_balance_mismatches(self, dust_factor: int) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_balance_mismatches(dust_factor=dust_factor) + + async def fetch_balance_with_balance_holds(self) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_balance_with_balance_holds() + + async def fetch_fee_discount_tier_statistics(self) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_fee_discount_tier_statistics() + + async def fetch_mito_vault_infos(self) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_mito_vault_infos() + + async def fetch_market_id_from_vault(self, vault_address: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_market_id_from_vault(vault_address=vault_address) + + async def fetch_historical_trade_records(self, market_id: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_historical_trade_records(market_id=market_id) + + async def fetch_is_opted_out_of_rewards(self, account: str) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_is_opted_out_of_rewards(account=account) + + async def fetch_opted_out_of_rewards_accounts(self) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_opted_out_of_rewards_accounts() + + async def fetch_market_volatility( + self, + market_id: str, + trade_grouping_sec: Optional[int] = None, + max_age: Optional[int] = None, + include_raw_history: Optional[bool] = None, + include_metadata: Optional[bool] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_market_volatility( + market_id=market_id, + trade_grouping_sec=trade_grouping_sec, + max_age=max_age, + include_raw_history=include_raw_history, + include_metadata=include_metadata, + ) + + async def fetch_chain_binary_options_markets(self, status: Optional[str] = None) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_binary_options_markets(status=status) + + async def fetch_trader_derivative_conditional_orders( + self, + subaccount_id: Optional[str] = None, + market_id: Optional[str] = None, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_trader_derivative_conditional_orders( + subaccount_id=subaccount_id, + market_id=market_id, + ) + + async def fetch_market_atomic_execution_fee_multiplier( + self, + market_id: str, + ) -> Dict[str, Any]: + return await self.chain_exchange_api.fetch_market_atomic_execution_fee_multiplier( + market_id=market_id, + ) + # Injective Exchange client methods # Auction RPC diff --git a/pyinjective/client/chain/grpc/chain_grpc_exchange_api.py b/pyinjective/client/chain/grpc/chain_grpc_exchange_api.py new file mode 100644 index 00000000..217c0d34 --- /dev/null +++ b/pyinjective/client/chain/grpc/chain_grpc_exchange_api.py @@ -0,0 +1,572 @@ +from typing import Any, Callable, Dict, List, Optional + +from grpc.aio import Channel + +from pyinjective.client.model.pagination import PaginationOption +from pyinjective.proto.injective.exchange.v1beta1 import ( + query_pb2 as exchange_query_pb, + query_pb2_grpc as exchange_query_grpc, +) +from pyinjective.utils.grpc_api_request_assistant import GrpcApiRequestAssistant + + +class ChainGrpcExchangeApi: + def __init__(self, channel: Channel, metadata_provider: Callable): + self._stub = exchange_query_grpc.QueryStub(channel) + self._assistant = GrpcApiRequestAssistant(metadata_provider=metadata_provider) + + async def fetch_exchange_params(self) -> Dict[str, Any]: + request = exchange_query_pb.QueryExchangeParamsRequest() + response = await self._execute_call(call=self._stub.QueryExchangeParams, request=request) + + return response + + async def fetch_subaccount_deposits( + self, + subaccount_id: Optional[str] = None, + subaccount_trader: Optional[str] = None, + subaccount_nonce: Optional[int] = None, + ) -> Dict[str, Any]: + subaccount = None + if subaccount_trader is not None or subaccount_nonce is not None: + subaccount = exchange_query_pb.Subaccount( + trader=subaccount_trader, + subaccount_nonce=subaccount_nonce, + ) + + request = exchange_query_pb.QuerySubaccountDepositsRequest(subaccount_id=subaccount_id, subaccount=subaccount) + response = await self._execute_call(call=self._stub.SubaccountDeposits, request=request) + + return response + + async def fetch_subaccount_deposit( + self, + subaccount_id: str, + denom: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QuerySubaccountDepositRequest( + subaccount_id=subaccount_id, + denom=denom, + ) + response = await self._execute_call(call=self._stub.SubaccountDeposit, request=request) + + return response + + async def fetch_exchange_balances(self) -> Dict[str, Any]: + request = exchange_query_pb.QueryExchangeBalancesRequest() + response = await self._execute_call(call=self._stub.ExchangeBalances, request=request) + + return response + + async def fetch_aggregate_volume(self, account: str) -> Dict[str, Any]: + request = exchange_query_pb.QueryAggregateVolumeRequest(account=account) + response = await self._execute_call(call=self._stub.AggregateVolume, request=request) + + return response + + async def fetch_aggregate_volumes( + self, + accounts: Optional[List[str]] = None, + market_ids: Optional[List[str]] = None, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryAggregateVolumesRequest(accounts=accounts, market_ids=market_ids) + response = await self._execute_call(call=self._stub.AggregateVolumes, request=request) + + return response + + async def fetch_aggregate_market_volume(self, market_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QueryAggregateMarketVolumeRequest(market_id=market_id) + response = await self._execute_call(call=self._stub.AggregateMarketVolume, request=request) + + return response + + async def fetch_aggregate_market_volumes(self, market_ids: Optional[List[str]] = None) -> Dict[str, Any]: + request = exchange_query_pb.QueryAggregateMarketVolumesRequest(market_ids=market_ids) + response = await self._execute_call(call=self._stub.AggregateMarketVolumes, request=request) + + return response + + async def fetch_denom_decimal(self, denom: str) -> Dict[str, Any]: + request = exchange_query_pb.QueryDenomDecimalRequest(denom=denom) + response = await self._execute_call(call=self._stub.DenomDecimal, request=request) + + return response + + async def fetch_denom_decimals(self, denoms: Optional[List[str]] = None) -> Dict[str, Any]: + request = exchange_query_pb.QueryDenomDecimalsRequest(denoms=denoms) + response = await self._execute_call(call=self._stub.DenomDecimals, request=request) + + return response + + async def fetch_spot_markets( + self, + status: Optional[str] = None, + market_ids: Optional[List[str]] = None, + ) -> Dict[str, Any]: + request = exchange_query_pb.QuerySpotMarketsRequest( + status=status, + market_ids=market_ids, + ) + response = await self._execute_call(call=self._stub.SpotMarkets, request=request) + + return response + + async def fetch_spot_market(self, market_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QuerySpotMarketRequest(market_id=market_id) + response = await self._execute_call(call=self._stub.SpotMarket, request=request) + + return response + + async def fetch_full_spot_markets( + self, + status: Optional[str] = None, + market_ids: Optional[List[str]] = None, + with_mid_price_and_tob: Optional[bool] = None, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryFullSpotMarketsRequest( + status=status, + market_ids=market_ids, + with_mid_price_and_tob=with_mid_price_and_tob, + ) + response = await self._execute_call(call=self._stub.FullSpotMarkets, request=request) + + return response + + async def fetch_full_spot_market( + self, + market_id: str, + with_mid_price_and_tob: Optional[bool] = None, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryFullSpotMarketRequest( + market_id=market_id, + with_mid_price_and_tob=with_mid_price_and_tob, + ) + response = await self._execute_call(call=self._stub.FullSpotMarket, request=request) + + return response + + async def fetch_spot_orderbook( + self, + market_id: str, + order_side: Optional[str] = None, + limit_cumulative_notional: Optional[str] = None, + limit_cumulative_quantity: Optional[str] = None, + pagination: Optional[PaginationOption] = None, + ) -> Dict[str, Any]: + limit = None + if pagination is not None: + limit = pagination.limit + request = exchange_query_pb.QuerySpotOrderbookRequest( + market_id=market_id, + order_side=order_side, + limit=limit, + limit_cumulative_notional=limit_cumulative_notional, + limit_cumulative_quantity=limit_cumulative_quantity, + ) + response = await self._execute_call(call=self._stub.SpotOrderbook, request=request) + + return response + + async def fetch_trader_spot_orders( + self, + market_id: str, + subaccount_id: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryTraderSpotOrdersRequest( + market_id=market_id, + subaccount_id=subaccount_id, + ) + response = await self._execute_call(call=self._stub.TraderSpotOrders, request=request) + + return response + + async def fetch_account_address_spot_orders( + self, + market_id: str, + account_address: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryAccountAddressSpotOrdersRequest( + market_id=market_id, + account_address=account_address, + ) + response = await self._execute_call(call=self._stub.AccountAddressSpotOrders, request=request) + + return response + + async def fetch_spot_orders_by_hashes( + self, + market_id: str, + subaccount_id: str, + order_hashes: List[str], + ) -> Dict[str, Any]: + request = exchange_query_pb.QuerySpotOrdersByHashesRequest( + market_id=market_id, + subaccount_id=subaccount_id, + order_hashes=order_hashes, + ) + response = await self._execute_call(call=self._stub.SpotOrdersByHashes, request=request) + + return response + + async def fetch_subaccount_orders( + self, + subaccount_id: str, + market_id: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QuerySubaccountOrdersRequest( + subaccount_id=subaccount_id, + market_id=market_id, + ) + response = await self._execute_call(call=self._stub.SubaccountOrders, request=request) + + return response + + async def fetch_trader_spot_transient_orders( + self, + market_id: str, + subaccount_id: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryTraderSpotOrdersRequest( + market_id=market_id, + subaccount_id=subaccount_id, + ) + response = await self._execute_call(call=self._stub.TraderSpotTransientOrders, request=request) + + return response + + async def fetch_spot_mid_price_and_tob( + self, + market_id: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QuerySpotMidPriceAndTOBRequest( + market_id=market_id, + ) + response = await self._execute_call(call=self._stub.SpotMidPriceAndTOB, request=request) + + return response + + async def fetch_derivative_mid_price_and_tob( + self, + market_id: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryDerivativeMidPriceAndTOBRequest( + market_id=market_id, + ) + response = await self._execute_call(call=self._stub.DerivativeMidPriceAndTOB, request=request) + + return response + + async def fetch_derivative_orderbook( + self, + market_id: str, + limit_cumulative_notional: Optional[str] = None, + pagination: Optional[PaginationOption] = None, + ) -> Dict[str, Any]: + limit = None + if pagination is not None: + limit = pagination.limit + request = exchange_query_pb.QueryDerivativeOrderbookRequest( + market_id=market_id, + limit=limit, + limit_cumulative_notional=limit_cumulative_notional, + ) + response = await self._execute_call(call=self._stub.DerivativeOrderbook, request=request) + + return response + + async def fetch_trader_derivative_orders( + self, + market_id: str, + subaccount_id: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryTraderDerivativeOrdersRequest( + market_id=market_id, + subaccount_id=subaccount_id, + ) + response = await self._execute_call(call=self._stub.TraderDerivativeOrders, request=request) + + return response + + async def fetch_account_address_derivative_orders( + self, + market_id: str, + account_address: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryAccountAddressDerivativeOrdersRequest( + market_id=market_id, + account_address=account_address, + ) + response = await self._execute_call(call=self._stub.AccountAddressDerivativeOrders, request=request) + + return response + + async def fetch_derivative_orders_by_hashes( + self, + market_id: str, + subaccount_id: str, + order_hashes: List[str], + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryDerivativeOrdersByHashesRequest( + market_id=market_id, + subaccount_id=subaccount_id, + order_hashes=order_hashes, + ) + response = await self._execute_call(call=self._stub.DerivativeOrdersByHashes, request=request) + + return response + + async def fetch_trader_derivative_transient_orders( + self, + market_id: str, + subaccount_id: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryTraderDerivativeOrdersRequest( + market_id=market_id, + subaccount_id=subaccount_id, + ) + response = await self._execute_call(call=self._stub.TraderDerivativeTransientOrders, request=request) + + return response + + async def fetch_derivative_markets( + self, + status: Optional[str] = None, + market_ids: Optional[List[str]] = None, + with_mid_price_and_tob: Optional[bool] = None, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryDerivativeMarketsRequest( + status=status, + market_ids=market_ids, + with_mid_price_and_tob=with_mid_price_and_tob, + ) + response = await self._execute_call(call=self._stub.DerivativeMarkets, request=request) + + return response + + async def fetch_derivative_market( + self, + market_id: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryDerivativeMarketRequest( + market_id=market_id, + ) + response = await self._execute_call(call=self._stub.DerivativeMarket, request=request) + + return response + + async def fetch_derivative_market_address( + self, + market_id: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryDerivativeMarketAddressRequest( + market_id=market_id, + ) + response = await self._execute_call(call=self._stub.DerivativeMarketAddress, request=request) + + return response + + async def fetch_subaccount_trade_nonce(self, subaccount_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QuerySubaccountTradeNonceRequest(subaccount_id=subaccount_id) + response = await self._execute_call(call=self._stub.SubaccountTradeNonce, request=request) + + return response + + async def fetch_positions(self) -> Dict[str, Any]: + request = exchange_query_pb.QueryPositionsRequest() + response = await self._execute_call(call=self._stub.Positions, request=request) + + return response + + async def fetch_subaccount_positions(self, subaccount_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QuerySubaccountPositionsRequest(subaccount_id=subaccount_id) + response = await self._execute_call(call=self._stub.SubaccountPositions, request=request) + + return response + + async def fetch_subaccount_position_in_market(self, subaccount_id: str, market_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QuerySubaccountPositionInMarketRequest( + subaccount_id=subaccount_id, + market_id=market_id, + ) + response = await self._execute_call(call=self._stub.SubaccountPositionInMarket, request=request) + + return response + + async def fetch_subaccount_effective_position_in_market(self, subaccount_id: str, market_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QuerySubaccountEffectivePositionInMarketRequest( + subaccount_id=subaccount_id, + market_id=market_id, + ) + response = await self._execute_call(call=self._stub.SubaccountEffectivePositionInMarket, request=request) + + return response + + async def fetch_perpetual_market_info(self, market_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QueryPerpetualMarketInfoRequest(market_id=market_id) + response = await self._execute_call(call=self._stub.PerpetualMarketInfo, request=request) + + return response + + async def fetch_expiry_futures_market_info(self, market_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QueryExpiryFuturesMarketInfoRequest(market_id=market_id) + response = await self._execute_call(call=self._stub.ExpiryFuturesMarketInfo, request=request) + + return response + + async def fetch_perpetual_market_funding(self, market_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QueryPerpetualMarketFundingRequest(market_id=market_id) + response = await self._execute_call(call=self._stub.PerpetualMarketFunding, request=request) + + return response + + async def fetch_subaccount_order_metadata(self, subaccount_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QuerySubaccountOrderMetadataRequest(subaccount_id=subaccount_id) + response = await self._execute_call(call=self._stub.SubaccountOrderMetadata, request=request) + + return response + + async def fetch_trade_reward_points( + self, + accounts: Optional[List[str]] = None, + pending_pool_timestamp: Optional[int] = None, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryTradeRewardPointsRequest( + accounts=accounts, + pending_pool_timestamp=pending_pool_timestamp, + ) + response = await self._execute_call(call=self._stub.TradeRewardPoints, request=request) + + return response + + async def fetch_pending_trade_reward_points( + self, + accounts: Optional[List[str]] = None, + pending_pool_timestamp: Optional[int] = None, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryTradeRewardPointsRequest( + accounts=accounts, + pending_pool_timestamp=pending_pool_timestamp, + ) + response = await self._execute_call(call=self._stub.PendingTradeRewardPoints, request=request) + + return response + + async def fetch_fee_discount_account_info( + self, + account: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryFeeDiscountAccountInfoRequest(account=account) + response = await self._execute_call(call=self._stub.FeeDiscountAccountInfo, request=request) + + return response + + async def fetch_fee_discount_schedule(self) -> Dict[str, Any]: + request = exchange_query_pb.QueryFeeDiscountScheduleRequest() + response = await self._execute_call(call=self._stub.FeeDiscountSchedule, request=request) + + return response + + async def fetch_balance_mismatches(self, dust_factor: int) -> Dict[str, Any]: + request = exchange_query_pb.QueryBalanceMismatchesRequest(dust_factor=dust_factor) + response = await self._execute_call(call=self._stub.BalanceMismatches, request=request) + + return response + + async def fetch_balance_with_balance_holds(self) -> Dict[str, Any]: + request = exchange_query_pb.QueryBalanceWithBalanceHoldsRequest() + response = await self._execute_call(call=self._stub.BalanceWithBalanceHolds, request=request) + + return response + + async def fetch_fee_discount_tier_statistics(self) -> Dict[str, Any]: + request = exchange_query_pb.QueryFeeDiscountTierStatisticsRequest() + response = await self._execute_call(call=self._stub.FeeDiscountTierStatistics, request=request) + + return response + + async def fetch_mito_vault_infos(self) -> Dict[str, Any]: + request = exchange_query_pb.MitoVaultInfosRequest() + response = await self._execute_call(call=self._stub.MitoVaultInfos, request=request) + + return response + + async def fetch_market_id_from_vault(self, vault_address: str) -> Dict[str, Any]: + request = exchange_query_pb.QueryMarketIDFromVaultRequest(vault_address=vault_address) + response = await self._execute_call(call=self._stub.QueryMarketIDFromVault, request=request) + + return response + + async def fetch_historical_trade_records(self, market_id: str) -> Dict[str, Any]: + request = exchange_query_pb.QueryHistoricalTradeRecordsRequest(market_id=market_id) + response = await self._execute_call(call=self._stub.HistoricalTradeRecords, request=request) + + return response + + async def fetch_is_opted_out_of_rewards(self, account: str) -> Dict[str, Any]: + request = exchange_query_pb.QueryIsOptedOutOfRewardsRequest(account=account) + response = await self._execute_call(call=self._stub.IsOptedOutOfRewards, request=request) + + return response + + async def fetch_opted_out_of_rewards_accounts(self) -> Dict[str, Any]: + request = exchange_query_pb.QueryOptedOutOfRewardsAccountsRequest() + response = await self._execute_call(call=self._stub.OptedOutOfRewardsAccounts, request=request) + + return response + + async def fetch_market_volatility( + self, + market_id: str, + trade_grouping_sec: Optional[int] = None, + max_age: Optional[int] = None, + include_raw_history: Optional[bool] = None, + include_metadata: Optional[bool] = None, + ) -> Dict[str, Any]: + trade_history_options = exchange_query_pb.TradeHistoryOptions() + if trade_grouping_sec is not None: + trade_history_options.trade_grouping_sec = trade_grouping_sec + if max_age is not None: + trade_history_options.max_age = max_age + if include_raw_history is not None: + trade_history_options.include_raw_history = include_raw_history + if include_metadata is not None: + trade_history_options.include_metadata = include_metadata + request = exchange_query_pb.QueryMarketVolatilityRequest( + market_id=market_id, trade_history_options=trade_history_options + ) + response = await self._execute_call(call=self._stub.MarketVolatility, request=request) + + return response + + async def fetch_binary_options_markets(self, status: Optional[str] = None) -> Dict[str, Any]: + request = exchange_query_pb.QueryBinaryMarketsRequest(status=status) + response = await self._execute_call(call=self._stub.BinaryOptionsMarkets, request=request) + + return response + + async def fetch_trader_derivative_conditional_orders( + self, + subaccount_id: Optional[str] = None, + market_id: Optional[str] = None, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryTraderDerivativeConditionalOrdersRequest( + subaccount_id=subaccount_id, + market_id=market_id, + ) + response = await self._execute_call(call=self._stub.TraderDerivativeConditionalOrders, request=request) + + return response + + async def fetch_market_atomic_execution_fee_multiplier( + self, + market_id: str, + ) -> Dict[str, Any]: + request = exchange_query_pb.QueryMarketAtomicExecutionFeeMultiplierRequest( + market_id=market_id, + ) + response = await self._execute_call(call=self._stub.MarketAtomicExecutionFeeMultiplier, request=request) + + return response + + async def _execute_call(self, call: Callable, request) -> Dict[str, Any]: + return await self._assistant.execute_call(call=call, request=request) diff --git a/tests/client/chain/grpc/configurable_exchange_query_servicer.py b/tests/client/chain/grpc/configurable_exchange_query_servicer.py new file mode 100644 index 00000000..267b08ce --- /dev/null +++ b/tests/client/chain/grpc/configurable_exchange_query_servicer.py @@ -0,0 +1,325 @@ +from collections import deque + +from pyinjective.proto.injective.exchange.v1beta1 import ( + query_pb2 as exchange_query_pb, + query_pb2_grpc as exchange_query_grpc, +) + + +class ConfigurableExchangeQueryServicer(exchange_query_grpc.QueryServicer): + def __init__(self): + super().__init__() + self.exchange_params = deque() + self.subaccount_deposits_responses = deque() + self.subaccount_deposit_responses = deque() + self.exchange_balances_responses = deque() + self.aggregate_volume_responses = deque() + self.aggregate_volumes_responses = deque() + self.aggregate_market_volume_responses = deque() + self.aggregate_market_volumes_responses = deque() + self.denom_decimal_responses = deque() + self.denom_decimals_responses = deque() + self.spot_markets_responses = deque() + self.spot_market_responses = deque() + self.full_spot_markets_responses = deque() + self.full_spot_market_responses = deque() + self.spot_orderbook_responses = deque() + self.trader_spot_orders_responses = deque() + self.account_address_spot_orders_responses = deque() + self.spot_orders_by_hashes_responses = deque() + self.subaccount_orders_responses = deque() + self.trader_spot_transient_orders_responses = deque() + self.spot_mid_price_and_tob_responses = deque() + self.derivative_mid_price_and_tob_responses = deque() + self.derivative_orderbook_responses = deque() + self.trader_derivative_orders_responses = deque() + self.account_address_derivative_orders_responses = deque() + self.derivative_orders_by_hashes_responses = deque() + self.trader_derivative_transient_orders_responses = deque() + self.derivative_markets_responses = deque() + self.derivative_market_responses = deque() + self.derivative_market_address_responses = deque() + self.subaccount_trade_nonce_responses = deque() + self.positions_responses = deque() + self.subaccount_positions_responses = deque() + self.subaccount_position_in_market_responses = deque() + self.subaccount_effective_position_in_market_responses = deque() + self.perpetual_market_info_responses = deque() + self.expiry_futures_market_info_responses = deque() + self.perpetual_market_funding_responses = deque() + self.subaccount_order_metadata_responses = deque() + self.trade_reward_points_responses = deque() + self.pending_trade_reward_points_responses = deque() + self.fee_discount_account_info_responses = deque() + self.fee_discount_schedule_responses = deque() + self.balance_mismatches_responses = deque() + self.balance_with_balance_holds_responses = deque() + self.fee_discount_tier_statistics_responses = deque() + self.mito_vault_infos_responses = deque() + self.market_id_from_vault_responses = deque() + self.historical_trade_records_responses = deque() + self.is_opted_out_of_rewards_responses = deque() + self.opted_out_of_rewards_accounts_responses = deque() + self.market_volatility_responses = deque() + self.binary_options_markets_responses = deque() + self.trader_derivative_conditional_orders_responses = deque() + self.market_atomic_execution_fee_multiplier_responses = deque() + + async def QueryExchangeParams( + self, request: exchange_query_pb.QueryExchangeParamsRequest, context=None, metadata=None + ): + return self.exchange_params.pop() + + async def SubaccountDeposits( + self, request: exchange_query_pb.QuerySubaccountDepositsRequest, context=None, metadata=None + ): + return self.subaccount_deposits_responses.pop() + + async def SubaccountDeposit( + self, request: exchange_query_pb.QuerySubaccountDepositRequest, context=None, metadata=None + ): + return self.subaccount_deposit_responses.pop() + + async def ExchangeBalances( + self, request: exchange_query_pb.QueryExchangeBalancesRequest, context=None, metadata=None + ): + return self.exchange_balances_responses.pop() + + async def AggregateVolume( + self, request: exchange_query_pb.QueryAggregateVolumeRequest, context=None, metadata=None + ): + return self.aggregate_volume_responses.pop() + + async def AggregateVolumes( + self, request: exchange_query_pb.QueryAggregateVolumesRequest, context=None, metadata=None + ): + return self.aggregate_volumes_responses.pop() + + async def AggregateMarketVolume( + self, request: exchange_query_pb.QueryAggregateMarketVolumeRequest, context=None, metadata=None + ): + return self.aggregate_market_volume_responses.pop() + + async def AggregateMarketVolumes( + self, request: exchange_query_pb.QueryAggregateMarketVolumesRequest, context=None, metadata=None + ): + return self.aggregate_market_volumes_responses.pop() + + async def DenomDecimal(self, request: exchange_query_pb.QueryDenomDecimalRequest, context=None, metadata=None): + return self.denom_decimal_responses.pop() + + async def DenomDecimals(self, request: exchange_query_pb.QueryDenomDecimalsRequest, context=None, metadata=None): + return self.denom_decimals_responses.pop() + + async def SpotMarkets(self, request: exchange_query_pb.QuerySpotMarketsRequest, context=None, metadata=None): + return self.spot_markets_responses.pop() + + async def SpotMarket(self, request: exchange_query_pb.QuerySpotMarketRequest, context=None, metadata=None): + return self.spot_market_responses.pop() + + async def FullSpotMarkets( + self, request: exchange_query_pb.QueryFullSpotMarketsRequest, context=None, metadata=None + ): + return self.full_spot_markets_responses.pop() + + async def FullSpotMarket(self, request: exchange_query_pb.QueryFullSpotMarketRequest, context=None, metadata=None): + return self.full_spot_market_responses.pop() + + async def SpotOrderbook(self, request: exchange_query_pb.QuerySpotOrderbookRequest, context=None, metadata=None): + return self.spot_orderbook_responses.pop() + + async def TraderSpotOrders( + self, request: exchange_query_pb.QueryTraderSpotOrdersRequest, context=None, metadata=None + ): + return self.trader_spot_orders_responses.pop() + + async def AccountAddressSpotOrders( + self, request: exchange_query_pb.QueryAccountAddressSpotOrdersRequest, context=None, metadata=None + ): + return self.account_address_spot_orders_responses.pop() + + async def SpotOrdersByHashes( + self, request: exchange_query_pb.QuerySpotOrdersByHashesRequest, context=None, metadata=None + ): + return self.spot_orders_by_hashes_responses.pop() + + async def SubaccountOrders( + self, request: exchange_query_pb.QuerySubaccountOrdersRequest, context=None, metadata=None + ): + return self.subaccount_orders_responses.pop() + + async def TraderSpotTransientOrders( + self, request: exchange_query_pb.QueryTraderSpotOrdersRequest, context=None, metadata=None + ): + return self.trader_spot_transient_orders_responses.pop() + + async def SpotMidPriceAndTOB( + self, request: exchange_query_pb.QuerySpotMidPriceAndTOBRequest, context=None, metadata=None + ): + return self.spot_mid_price_and_tob_responses.pop() + + async def DerivativeMidPriceAndTOB( + self, request: exchange_query_pb.QueryDerivativeMidPriceAndTOBRequest, context=None, metadata=None + ): + return self.derivative_mid_price_and_tob_responses.pop() + + async def DerivativeOrderbook( + self, request: exchange_query_pb.QueryDerivativeOrderbookRequest, context=None, metadata=None + ): + return self.derivative_orderbook_responses.pop() + + async def TraderDerivativeOrders( + self, request: exchange_query_pb.QueryTraderDerivativeOrdersRequest, context=None, metadata=None + ): + return self.trader_derivative_orders_responses.pop() + + async def AccountAddressDerivativeOrders( + self, request: exchange_query_pb.QueryAccountAddressDerivativeOrdersRequest, context=None, metadata=None + ): + return self.account_address_derivative_orders_responses.pop() + + async def DerivativeOrdersByHashes( + self, request: exchange_query_pb.QueryDerivativeOrdersByHashesRequest, context=None, metadata=None + ): + return self.derivative_orders_by_hashes_responses.pop() + + async def TraderDerivativeTransientOrders( + self, request: exchange_query_pb.QueryTraderDerivativeOrdersRequest, context=None, metadata=None + ): + return self.trader_derivative_transient_orders_responses.pop() + + async def DerivativeMarkets( + self, request: exchange_query_pb.QueryDerivativeMarketsRequest, context=None, metadata=None + ): + return self.derivative_markets_responses.pop() + + async def DerivativeMarket( + self, request: exchange_query_pb.QueryDerivativeMarketRequest, context=None, metadata=None + ): + return self.derivative_market_responses.pop() + + async def DerivativeMarketAddress( + self, request: exchange_query_pb.QueryDerivativeMarketAddressRequest, context=None, metadata=None + ): + return self.derivative_market_address_responses.pop() + + async def SubaccountTradeNonce( + self, request: exchange_query_pb.QuerySubaccountTradeNonceRequest, context=None, metadata=None + ): + return self.subaccount_trade_nonce_responses.pop() + + async def Positions(self, request: exchange_query_pb.QueryPositionsRequest, context=None, metadata=None): + return self.positions_responses.pop() + + async def SubaccountPositions( + self, request: exchange_query_pb.QuerySubaccountPositionsRequest, context=None, metadata=None + ): + return self.subaccount_positions_responses.pop() + + async def SubaccountPositionInMarket( + self, request: exchange_query_pb.QuerySubaccountPositionInMarketRequest, context=None, metadata=None + ): + return self.subaccount_position_in_market_responses.pop() + + async def SubaccountEffectivePositionInMarket( + self, request: exchange_query_pb.QuerySubaccountEffectivePositionInMarketRequest, context=None, metadata=None + ): + return self.subaccount_effective_position_in_market_responses.pop() + + async def PerpetualMarketInfo( + self, request: exchange_query_pb.QueryPerpetualMarketInfoRequest, context=None, metadata=None + ): + return self.perpetual_market_info_responses.pop() + + async def ExpiryFuturesMarketInfo( + self, request: exchange_query_pb.QueryExpiryFuturesMarketInfoRequest, context=None, metadata=None + ): + return self.expiry_futures_market_info_responses.pop() + + async def PerpetualMarketFunding( + self, request: exchange_query_pb.QueryPerpetualMarketFundingRequest, context=None, metadata=None + ): + return self.perpetual_market_funding_responses.pop() + + async def SubaccountOrderMetadata( + self, request: exchange_query_pb.QuerySubaccountOrderMetadataRequest, context=None, metadata=None + ): + return self.subaccount_order_metadata_responses.pop() + + async def TradeRewardPoints( + self, request: exchange_query_pb.QueryTradeRewardPointsRequest, context=None, metadata=None + ): + return self.trade_reward_points_responses.pop() + + async def PendingTradeRewardPoints( + self, request: exchange_query_pb.QueryTradeRewardPointsRequest, context=None, metadata=None + ): + return self.pending_trade_reward_points_responses.pop() + + async def FeeDiscountAccountInfo( + self, request: exchange_query_pb.QueryFeeDiscountAccountInfoRequest, context=None, metadata=None + ): + return self.fee_discount_account_info_responses.pop() + + async def FeeDiscountSchedule( + self, request: exchange_query_pb.QueryFeeDiscountScheduleRequest, context=None, metadata=None + ): + return self.fee_discount_schedule_responses.pop() + + async def BalanceMismatches( + self, request: exchange_query_pb.QueryBalanceMismatchesRequest, context=None, metadata=None + ): + return self.balance_mismatches_responses.pop() + + async def BalanceWithBalanceHolds( + self, request: exchange_query_pb.QueryBalanceWithBalanceHoldsRequest, context=None, metadata=None + ): + return self.balance_with_balance_holds_responses.pop() + + async def FeeDiscountTierStatistics( + self, request: exchange_query_pb.QueryFeeDiscountTierStatisticsRequest, context=None, metadata=None + ): + return self.fee_discount_tier_statistics_responses.pop() + + async def MitoVaultInfos(self, request: exchange_query_pb.MitoVaultInfosRequest, context=None, metadata=None): + return self.mito_vault_infos_responses.pop() + + async def QueryMarketIDFromVault( + self, request: exchange_query_pb.QueryMarketIDFromVaultRequest, context=None, metadata=None + ): + return self.market_id_from_vault_responses.pop() + + async def HistoricalTradeRecords( + self, request: exchange_query_pb.QueryHistoricalTradeRecordsRequest, context=None, metadata=None + ): + return self.historical_trade_records_responses.pop() + + async def IsOptedOutOfRewards( + self, request: exchange_query_pb.QueryIsOptedOutOfRewardsRequest, context=None, metadata=None + ): + return self.is_opted_out_of_rewards_responses.pop() + + async def OptedOutOfRewardsAccounts( + self, request: exchange_query_pb.QueryOptedOutOfRewardsAccountsRequest, context=None, metadata=None + ): + return self.opted_out_of_rewards_accounts_responses.pop() + + async def MarketVolatility( + self, request: exchange_query_pb.QueryMarketVolatilityRequest, context=None, metadata=None + ): + return self.market_volatility_responses.pop() + + async def BinaryOptionsMarkets( + self, request: exchange_query_pb.QueryBinaryMarketsRequest, context=None, metadata=None + ): + return self.binary_options_markets_responses.pop() + + async def TraderDerivativeConditionalOrders( + self, request: exchange_query_pb.QueryTraderDerivativeConditionalOrdersRequest, context=None, metadata=None + ): + return self.trader_derivative_conditional_orders_responses.pop() + + async def MarketAtomicExecutionFeeMultiplier( + self, request: exchange_query_pb.QueryMarketAtomicExecutionFeeMultiplierRequest, context=None, metadata=None + ): + return self.market_atomic_execution_fee_multiplier_responses.pop() diff --git a/tests/client/chain/grpc/test_chain_grpc_exchange_api.py b/tests/client/chain/grpc/test_chain_grpc_exchange_api.py new file mode 100644 index 00000000..54393a94 --- /dev/null +++ b/tests/client/chain/grpc/test_chain_grpc_exchange_api.py @@ -0,0 +1,2466 @@ +import base64 + +import grpc +import pytest + +from pyinjective.client.chain.grpc.chain_grpc_exchange_api import ChainGrpcExchangeApi +from pyinjective.client.model.pagination import PaginationOption +from pyinjective.core.network import Network +from pyinjective.proto.cosmos.base.v1beta1 import coin_pb2 as coin_pb +from pyinjective.proto.injective.exchange.v1beta1 import ( + exchange_pb2 as exchange_pb, + genesis_pb2 as genesis_pb, + query_pb2 as exchange_query_pb, +) +from pyinjective.proto.injective.oracle.v1beta1 import oracle_pb2 as oracle_pb +from tests.client.chain.grpc.configurable_exchange_query_servicer import ConfigurableExchangeQueryServicer + + +@pytest.fixture +def exchange_servicer(): + return ConfigurableExchangeQueryServicer() + + +class TestChainGrpcBankApi: + @pytest.mark.asyncio + async def test_fetch_exchange_params( + self, + exchange_servicer, + ): + spot_market_instant_listing_fee = coin_pb.Coin(denom="inj", amount="10000000000000000000") + derivative_market_instant_listing_fee = coin_pb.Coin(denom="inj", amount="2000000000000000000000") + binary_options_market_instant_listing_fee = coin_pb.Coin(denom="inj", amount="30000000000000000000") + params = exchange_pb.Params( + spot_market_instant_listing_fee=spot_market_instant_listing_fee, + derivative_market_instant_listing_fee=derivative_market_instant_listing_fee, + default_spot_maker_fee_rate="-0.000100000000000000", + default_spot_taker_fee_rate="0.001000000000000000", + default_derivative_maker_fee_rate="-0.000100000000000000", + default_derivative_taker_fee_rate="0.001000000000000000", + default_initial_margin_ratio="0.050000000000000000", + default_maintenance_margin_ratio="0.020000000000000000", + default_funding_interval=3600, + funding_multiple=4600, + relayer_fee_share_rate="0.400000000000000000", + default_hourly_funding_rate_cap="0.000625000000000000", + default_hourly_interest_rate="0.000004166660000000", + max_derivative_order_side_count=20, + inj_reward_staked_requirement_threshold="25000000000000000000", + trading_rewards_vesting_duration=1209600, + liquidator_reward_share_rate="0.050000000000000000", + binary_options_market_instant_listing_fee=binary_options_market_instant_listing_fee, + atomic_market_order_access_level=2, + spot_atomic_market_order_fee_multiplier="2.000000000000000000", + derivative_atomic_market_order_fee_multiplier="2.000000000000000000", + binary_options_atomic_market_order_fee_multiplier="2.000000000000000000", + minimal_protocol_fee_rate="0.000010000000000000", + is_instant_derivative_market_launch_enabled=False, + post_only_mode_height_threshold=57078000, + ) + exchange_servicer.exchange_params.append(exchange_query_pb.QueryExchangeParamsResponse(params=params)) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + module_params = await api.fetch_exchange_params() + expected_params = { + "params": { + "spotMarketInstantListingFee": { + "amount": spot_market_instant_listing_fee.amount, + "denom": spot_market_instant_listing_fee.denom, + }, + "derivativeMarketInstantListingFee": { + "amount": derivative_market_instant_listing_fee.amount, + "denom": derivative_market_instant_listing_fee.denom, + }, + "defaultSpotMakerFeeRate": params.default_spot_maker_fee_rate, + "defaultSpotTakerFeeRate": params.default_spot_taker_fee_rate, + "defaultDerivativeMakerFeeRate": params.default_derivative_maker_fee_rate, + "defaultDerivativeTakerFeeRate": params.default_derivative_taker_fee_rate, + "defaultInitialMarginRatio": params.default_initial_margin_ratio, + "defaultMaintenanceMarginRatio": params.default_maintenance_margin_ratio, + "defaultFundingInterval": str(params.default_funding_interval), + "fundingMultiple": str(params.funding_multiple), + "relayerFeeShareRate": params.relayer_fee_share_rate, + "defaultHourlyFundingRateCap": params.default_hourly_funding_rate_cap, + "defaultHourlyInterestRate": params.default_hourly_interest_rate, + "maxDerivativeOrderSideCount": params.max_derivative_order_side_count, + "injRewardStakedRequirementThreshold": params.inj_reward_staked_requirement_threshold, + "tradingRewardsVestingDuration": str(params.trading_rewards_vesting_duration), + "liquidatorRewardShareRate": "0.050000000000000000", + "binaryOptionsMarketInstantListingFee": { + "amount": binary_options_market_instant_listing_fee.amount, + "denom": binary_options_market_instant_listing_fee.denom, + }, + "atomicMarketOrderAccessLevel": exchange_pb.AtomicMarketOrderAccessLevel.Name( + params.atomic_market_order_access_level + ), + "spotAtomicMarketOrderFeeMultiplier": params.spot_atomic_market_order_fee_multiplier, + "derivativeAtomicMarketOrderFeeMultiplier": params.derivative_atomic_market_order_fee_multiplier, + "binaryOptionsAtomicMarketOrderFeeMultiplier": params.binary_options_atomic_market_order_fee_multiplier, + "minimalProtocolFeeRate": params.minimal_protocol_fee_rate, + "isInstantDerivativeMarketLaunchEnabled": params.is_instant_derivative_market_launch_enabled, + "postOnlyModeHeightThreshold": str(params.post_only_mode_height_threshold), + } + } + + assert module_params == expected_params + + @pytest.mark.asyncio + async def test_fetch_subaccount_deposits( + self, + exchange_servicer, + ): + deposit_denom = "inj" + deposit = exchange_pb.Deposit( + available_balance="1000000000000000000", + total_balance="2000000000000000000", + ) + exchange_servicer.subaccount_deposits_responses.append( + exchange_query_pb.QuerySubaccountDepositsResponse(deposits={deposit_denom: deposit}) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + deposits = await api.fetch_subaccount_deposits( + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + subaccount_trader="inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7", + subaccount_nonce=1, + ) + expected_deposits = { + "deposits": { + deposit_denom: { + "availableBalance": deposit.available_balance, + "totalBalance": deposit.total_balance, + }, + } + } + + assert deposits == expected_deposits + + @pytest.mark.asyncio + async def test_fetch_subaccount_deposit( + self, + exchange_servicer, + ): + deposit = exchange_pb.Deposit( + available_balance="1000000000000000000", + total_balance="2000000000000000000", + ) + exchange_servicer.subaccount_deposit_responses.append( + exchange_query_pb.QuerySubaccountDepositResponse(deposits=deposit) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + deposit_response = await api.fetch_subaccount_deposit( + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + denom="inj", + ) + expected_deposit = { + "deposits": { + "availableBalance": deposit.available_balance, + "totalBalance": deposit.total_balance, + } + } + + assert deposit_response == expected_deposit + + @pytest.mark.asyncio + async def test_fetch_exchange_balances( + self, + exchange_servicer, + ): + deposit = exchange_pb.Deposit( + available_balance="1000000000000000000", + total_balance="2000000000000000000", + ) + balance = genesis_pb.Balance( + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + denom="inj", + deposits=deposit, + ) + exchange_servicer.exchange_balances_responses.append( + exchange_query_pb.QueryExchangeBalancesResponse(balances=[balance]) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + balances_response = await api.fetch_exchange_balances() + expected_balances = { + "balances": [ + { + "subaccountId": balance.subaccount_id, + "denom": balance.denom, + "deposits": { + "availableBalance": deposit.available_balance, + "totalBalance": deposit.total_balance, + }, + }, + ] + } + + assert balances_response == expected_balances + + @pytest.mark.asyncio + async def test_fetch_aggregate_volume( + self, + exchange_servicer, + ): + volume = exchange_pb.VolumeRecord( + maker_volume="1000000000000000000", + taker_volume="2000000000000000000", + ) + market_volume = exchange_pb.MarketVolume( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + volume=volume, + ) + exchange_servicer.aggregate_volume_responses.append( + exchange_query_pb.QueryAggregateVolumeResponse( + aggregate_volumes=[market_volume], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + volume_response = await api.fetch_aggregate_volume(account="inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r") + expected_volume = { + "aggregateVolumes": [ + { + "marketId": market_volume.market_id, + "volume": { + "makerVolume": volume.maker_volume, + "takerVolume": volume.taker_volume, + }, + }, + ] + } + + assert volume_response == expected_volume + + @pytest.mark.asyncio + async def test_fetch_aggregate_volumes( + self, + exchange_servicer, + ): + acc_volume = exchange_pb.VolumeRecord( + maker_volume="1000000000000000000", + taker_volume="2000000000000000000", + ) + account_market_volume = exchange_pb.MarketVolume( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + volume=acc_volume, + ) + account_volume = exchange_pb.AggregateAccountVolumeRecord( + account="inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r", + market_volumes=[account_market_volume], + ) + volume = exchange_pb.VolumeRecord( + maker_volume="3000000000000000000", + taker_volume="4000000000000000000", + ) + market_volume = exchange_pb.MarketVolume( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + volume=volume, + ) + exchange_servicer.aggregate_volumes_responses.append( + exchange_query_pb.QueryAggregateVolumesResponse( + aggregate_account_volumes=[account_volume], + aggregate_market_volumes=[market_volume], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + volume_response = await api.fetch_aggregate_volumes( + accounts=[account_volume.account], + market_ids=[account_market_volume.market_id], + ) + expected_volume = { + "aggregateAccountVolumes": [ + { + "account": account_volume.account, + "marketVolumes": [ + { + "marketId": account_market_volume.market_id, + "volume": { + "makerVolume": acc_volume.maker_volume, + "takerVolume": acc_volume.taker_volume, + }, + }, + ], + }, + ], + "aggregateMarketVolumes": [ + { + "marketId": market_volume.market_id, + "volume": { + "makerVolume": volume.maker_volume, + "takerVolume": volume.taker_volume, + }, + }, + ], + } + + assert volume_response == expected_volume + + @pytest.mark.asyncio + async def test_fetch_aggregate_market_volume( + self, + exchange_servicer, + ): + volume = exchange_pb.VolumeRecord( + maker_volume="1000000000000000000", + taker_volume="2000000000000000000", + ) + exchange_servicer.aggregate_market_volume_responses.append( + exchange_query_pb.QueryAggregateMarketVolumeResponse( + volume=volume, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + volume_response = await api.fetch_aggregate_market_volume( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe" + ) + expected_volume = { + "volume": { + "makerVolume": volume.maker_volume, + "takerVolume": volume.taker_volume, + } + } + + assert volume_response == expected_volume + + @pytest.mark.asyncio + async def test_fetch_aggregate_market_volumes( + self, + exchange_servicer, + ): + volume = exchange_pb.VolumeRecord( + maker_volume="3000000000000000000", + taker_volume="4000000000000000000", + ) + market_volume = exchange_pb.MarketVolume( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + volume=volume, + ) + exchange_servicer.aggregate_market_volumes_responses.append( + exchange_query_pb.QueryAggregateMarketVolumesResponse( + volumes=[market_volume], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + volume_response = await api.fetch_aggregate_market_volumes( + market_ids=[market_volume.market_id], + ) + expected_volume = { + "volumes": [ + { + "marketId": market_volume.market_id, + "volume": { + "makerVolume": volume.maker_volume, + "takerVolume": volume.taker_volume, + }, + }, + ], + } + + assert volume_response == expected_volume + + @pytest.mark.asyncio + async def test_fetch_denom_decimal( + self, + exchange_servicer, + ): + decimal = 18 + exchange_servicer.denom_decimal_responses.append( + exchange_query_pb.QueryDenomDecimalResponse( + decimal=decimal, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + denom_decimal = await api.fetch_denom_decimal(denom="inj") + expected_decimal = {"decimal": str(decimal)} + + assert denom_decimal == expected_decimal + + @pytest.mark.asyncio + async def test_fetch_denom_decimals( + self, + exchange_servicer, + ): + denom_decimal = exchange_pb.DenomDecimals( + denom="inj", + decimals=18, + ) + exchange_servicer.denom_decimals_responses.append( + exchange_query_pb.QueryDenomDecimalsResponse( + denom_decimals=[denom_decimal], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + denom_decimals = await api.fetch_denom_decimals(denoms=[denom_decimal.denom]) + expected_decimals = { + "denomDecimals": [ + { + "denom": denom_decimal.denom, + "decimals": str(denom_decimal.decimals), + } + ] + } + + assert denom_decimals == expected_decimals + + @pytest.mark.asyncio + async def test_fetch_spot_markets( + self, + exchange_servicer, + ): + market = exchange_pb.SpotMarket( + ticker="INJ/USDT", + base_denom="inj", + quote_denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5", + maker_fee_rate="-0.0001", + taker_fee_rate="0.001", + relayer_fee_share_rate="0.4", + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + status=1, + min_price_tick_size="0.000000000000001", + min_quantity_tick_size="1000000000000000", + ) + exchange_servicer.spot_markets_responses.append( + exchange_query_pb.QuerySpotMarketsResponse( + markets=[market], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + status_string = exchange_pb.MarketStatus.Name(market.status) + markets = await api.fetch_spot_markets( + status=status_string, + market_ids=[market.market_id], + ) + expected_markets = { + "markets": [ + { + "ticker": market.ticker, + "baseDenom": market.base_denom, + "quoteDenom": market.quote_denom, + "makerFeeRate": market.maker_fee_rate, + "takerFeeRate": market.taker_fee_rate, + "relayerFeeShareRate": market.relayer_fee_share_rate, + "marketId": market.market_id, + "status": status_string, + "minPriceTickSize": market.min_price_tick_size, + "minQuantityTickSize": market.min_quantity_tick_size, + } + ] + } + + assert markets == expected_markets + + @pytest.mark.asyncio + async def test_fetch_spot_market( + self, + exchange_servicer, + ): + market = exchange_pb.SpotMarket( + ticker="INJ/USDT", + base_denom="inj", + quote_denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5", + maker_fee_rate="-0.0001", + taker_fee_rate="0.001", + relayer_fee_share_rate="0.4", + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + status=1, + min_price_tick_size="0.000000000000001", + min_quantity_tick_size="1000000000000000", + ) + exchange_servicer.spot_market_responses.append( + exchange_query_pb.QuerySpotMarketResponse( + market=market, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + response_market = await api.fetch_spot_market( + market_id=market.market_id, + ) + expected_market = { + "market": { + "ticker": market.ticker, + "baseDenom": market.base_denom, + "quoteDenom": market.quote_denom, + "makerFeeRate": market.maker_fee_rate, + "takerFeeRate": market.taker_fee_rate, + "relayerFeeShareRate": market.relayer_fee_share_rate, + "marketId": market.market_id, + "status": exchange_pb.MarketStatus.Name(market.status), + "minPriceTickSize": market.min_price_tick_size, + "minQuantityTickSize": market.min_quantity_tick_size, + } + } + + assert response_market == expected_market + + @pytest.mark.asyncio + async def test_fetch_full_spot_markets( + self, + exchange_servicer, + ): + market = exchange_pb.SpotMarket( + ticker="INJ/USDT", + base_denom="inj", + quote_denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5", + maker_fee_rate="-0.0001", + taker_fee_rate="0.001", + relayer_fee_share_rate="0.4", + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + status=1, + min_price_tick_size="0.000000000000001", + min_quantity_tick_size="1000000000000000", + ) + mid_price_and_tob = exchange_pb.MidPriceAndTOB( + mid_price="2000000000000000000", + best_buy_price="1000000000000000000", + best_sell_price="3000000000000000000", + ) + full_market = exchange_query_pb.FullSpotMarket( + market=market, + mid_price_and_tob=mid_price_and_tob, + ) + exchange_servicer.full_spot_markets_responses.append( + exchange_query_pb.QueryFullSpotMarketsResponse( + markets=[full_market], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + status_string = exchange_pb.MarketStatus.Name(market.status) + markets = await api.fetch_full_spot_markets( + status=status_string, + market_ids=[market.market_id], + with_mid_price_and_tob=True, + ) + expected_markets = { + "markets": [ + { + "market": { + "ticker": market.ticker, + "baseDenom": market.base_denom, + "quoteDenom": market.quote_denom, + "makerFeeRate": market.maker_fee_rate, + "takerFeeRate": market.taker_fee_rate, + "relayerFeeShareRate": market.relayer_fee_share_rate, + "marketId": market.market_id, + "status": status_string, + "minPriceTickSize": market.min_price_tick_size, + "minQuantityTickSize": market.min_quantity_tick_size, + }, + "midPriceAndTob": { + "midPrice": mid_price_and_tob.mid_price, + "bestBuyPrice": mid_price_and_tob.best_buy_price, + "bestSellPrice": mid_price_and_tob.best_sell_price, + }, + } + ] + } + + assert markets == expected_markets + + @pytest.mark.asyncio + async def test_fetch_full_spot_market( + self, + exchange_servicer, + ): + market = exchange_pb.SpotMarket( + ticker="INJ/USDT", + base_denom="inj", + quote_denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5", + maker_fee_rate="-0.0001", + taker_fee_rate="0.001", + relayer_fee_share_rate="0.4", + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + status=1, + min_price_tick_size="0.000000000000001", + min_quantity_tick_size="1000000000000000", + ) + mid_price_and_tob = exchange_pb.MidPriceAndTOB( + mid_price="2000000000000000000", + best_buy_price="1000000000000000000", + best_sell_price="3000000000000000000", + ) + full_market = exchange_query_pb.FullSpotMarket( + market=market, + mid_price_and_tob=mid_price_and_tob, + ) + exchange_servicer.full_spot_market_responses.append( + exchange_query_pb.QueryFullSpotMarketResponse( + market=full_market, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + status_string = exchange_pb.MarketStatus.Name(market.status) + market_response = await api.fetch_full_spot_market( + market_id=market.market_id, + with_mid_price_and_tob=True, + ) + expected_market = { + "market": { + "market": { + "ticker": market.ticker, + "baseDenom": market.base_denom, + "quoteDenom": market.quote_denom, + "makerFeeRate": market.maker_fee_rate, + "takerFeeRate": market.taker_fee_rate, + "relayerFeeShareRate": market.relayer_fee_share_rate, + "marketId": market.market_id, + "status": status_string, + "minPriceTickSize": market.min_price_tick_size, + "minQuantityTickSize": market.min_quantity_tick_size, + }, + "midPriceAndTob": { + "midPrice": mid_price_and_tob.mid_price, + "bestBuyPrice": mid_price_and_tob.best_buy_price, + "bestSellPrice": mid_price_and_tob.best_sell_price, + }, + } + } + + assert market_response == expected_market + + @pytest.mark.asyncio + async def test_fetch_spot_orderbook( + self, + exchange_servicer, + ): + buy_price_level = exchange_pb.Level( + p="1000000000000000000", + q="1000000000000000", + ) + sell_price_level = exchange_pb.Level( + p="2000000000000000000", + q="2000000000000000", + ) + exchange_servicer.spot_orderbook_responses.append( + exchange_query_pb.QuerySpotOrderbookResponse( + buys_price_level=[buy_price_level], + sells_price_level=[sell_price_level], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orderbook = await api.fetch_spot_orderbook( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + order_side="Side_Unspecified", + limit_cumulative_notional="1000000000000000000", + limit_cumulative_quantity="1000000000000000", + pagination=PaginationOption(limit=100), + ) + expected_orderbook = { + "buysPriceLevel": [ + { + "p": buy_price_level.p, + "q": buy_price_level.q, + } + ], + "sellsPriceLevel": [ + { + "p": sell_price_level.p, + "q": sell_price_level.q, + } + ], + } + + assert orderbook == expected_orderbook + + @pytest.mark.asyncio + async def test_fetch_trader_spot_orders( + self, + exchange_servicer, + ): + order = exchange_query_pb.TrimmedSpotLimitOrder( + price="1000000000000000000", + quantity="1000000000000000", + fillable="1000000000000000", + isBuy=True, + order_hash="0x14e43adbb3302db28bcd0619068227ebca880cdd66cdfc8b4a662bcac0777849", + ) + exchange_servicer.trader_spot_orders_responses.append( + exchange_query_pb.QueryTraderSpotOrdersResponse( + orders=[order], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orders = await api.fetch_trader_spot_orders( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + ) + expected_orders = { + "orders": [ + { + "price": order.price, + "quantity": order.quantity, + "fillable": order.fillable, + "isBuy": order.isBuy, + "orderHash": order.order_hash, + } + ] + } + + assert orders == expected_orders + + @pytest.mark.asyncio + async def test_fetch_account_address_spot_orders( + self, + exchange_servicer, + ): + order = exchange_query_pb.TrimmedSpotLimitOrder( + price="1000000000000000000", + quantity="1000000000000000", + fillable="1000000000000000", + isBuy=True, + order_hash="0x14e43adbb3302db28bcd0619068227ebca880cdd66cdfc8b4a662bcac0777849", + ) + exchange_servicer.account_address_spot_orders_responses.append( + exchange_query_pb.QueryAccountAddressSpotOrdersResponse( + orders=[order], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orders = await api.fetch_account_address_spot_orders( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + account_address="inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7", + ) + expected_orders = { + "orders": [ + { + "price": order.price, + "quantity": order.quantity, + "fillable": order.fillable, + "isBuy": order.isBuy, + "orderHash": order.order_hash, + } + ] + } + + assert orders == expected_orders + + @pytest.mark.asyncio + async def test_fetch_spot_orders_by_hashes( + self, + exchange_servicer, + ): + order = exchange_query_pb.TrimmedSpotLimitOrder( + price="1000000000000000000", + quantity="1000000000000000", + fillable="1000000000000000", + isBuy=True, + order_hash="0x14e43adbb3302db28bcd0619068227ebca880cdd66cdfc8b4a662bcac0777849", + ) + exchange_servicer.spot_orders_by_hashes_responses.append( + exchange_query_pb.QuerySpotOrdersByHashesResponse( + orders=[order], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orders = await api.fetch_spot_orders_by_hashes( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + order_hashes=[order.order_hash], + ) + expected_orders = { + "orders": [ + { + "price": order.price, + "quantity": order.quantity, + "fillable": order.fillable, + "isBuy": order.isBuy, + "orderHash": order.order_hash, + } + ] + } + + assert orders == expected_orders + + @pytest.mark.asyncio + async def test_fetch_subaccount_orders( + self, + exchange_servicer, + ): + buy_subaccount_order = exchange_pb.SubaccountOrder( + price="1000000000000000000", + quantity="1000000000000000", + isReduceOnly=False, + ) + buy_order = exchange_pb.SubaccountOrderData( + order=buy_subaccount_order, + order_hash="0x14e43adbb3302db28bcd0619068227ebca880cdd66cdfc8b4a662bcac0777849".encode(), + ) + sell_subaccount_order = exchange_pb.SubaccountOrder( + price="2000000000000000000", + quantity="2000000000000000", + isReduceOnly=False, + ) + sell_order = exchange_pb.SubaccountOrderData( + order=sell_subaccount_order, + order_hash="0x222daa22f60fe9f075ed0ca583459e121c23e64431c3fbffdedda04598ede0d2".encode(), + ) + exchange_servicer.subaccount_orders_responses.append( + exchange_query_pb.QuerySubaccountOrdersResponse( + buy_orders=[buy_order], + sell_orders=[sell_order], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orders = await api.fetch_subaccount_orders( + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + ) + expected_orders = { + "buyOrders": [ + { + "order": { + "price": buy_subaccount_order.price, + "quantity": buy_subaccount_order.quantity, + "isReduceOnly": buy_subaccount_order.isReduceOnly, + }, + "orderHash": base64.b64encode(buy_order.order_hash).decode(), + } + ], + "sellOrders": [ + { + "order": { + "price": sell_subaccount_order.price, + "quantity": sell_subaccount_order.quantity, + "isReduceOnly": sell_subaccount_order.isReduceOnly, + }, + "orderHash": base64.b64encode(sell_order.order_hash).decode(), + } + ], + } + + assert orders == expected_orders + + @pytest.mark.asyncio + async def test_fetch_trader_spot_transient_orders( + self, + exchange_servicer, + ): + order = exchange_query_pb.TrimmedSpotLimitOrder( + price="1000000000000000000", + quantity="1000000000000000", + fillable="1000000000000000", + isBuy=True, + order_hash="0x14e43adbb3302db28bcd0619068227ebca880cdd66cdfc8b4a662bcac0777849", + ) + exchange_servicer.trader_spot_transient_orders_responses.append( + exchange_query_pb.QueryTraderSpotOrdersResponse( + orders=[order], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orders = await api.fetch_trader_spot_transient_orders( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + ) + expected_orders = { + "orders": [ + { + "price": order.price, + "quantity": order.quantity, + "fillable": order.fillable, + "isBuy": order.isBuy, + "orderHash": order.order_hash, + } + ] + } + + assert orders == expected_orders + + @pytest.mark.asyncio + async def test_fetch_spot_mid_price_and_tob( + self, + exchange_servicer, + ): + response = exchange_query_pb.QuerySpotMidPriceAndTOBResponse( + mid_price="2000000000000000000", + best_buy_price="1000000000000000000", + best_sell_price="3000000000000000000", + ) + exchange_servicer.spot_mid_price_and_tob_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + prices = await api.fetch_spot_mid_price_and_tob( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + ) + expected_prices = { + "midPrice": response.mid_price, + "bestBuyPrice": response.best_buy_price, + "bestSellPrice": response.best_sell_price, + } + + assert prices == expected_prices + + @pytest.mark.asyncio + async def test_fetch_derivative_mid_price_and_tob( + self, + exchange_servicer, + ): + response = exchange_query_pb.QueryDerivativeMidPriceAndTOBResponse( + mid_price="2000000000000000000", + best_buy_price="1000000000000000000", + best_sell_price="3000000000000000000", + ) + exchange_servicer.derivative_mid_price_and_tob_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + prices = await api.fetch_derivative_mid_price_and_tob( + market_id="0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe", + ) + expected_prices = { + "midPrice": response.mid_price, + "bestBuyPrice": response.best_buy_price, + "bestSellPrice": response.best_sell_price, + } + + assert prices == expected_prices + + @pytest.mark.asyncio + async def test_fetch_derivative_orderbook( + self, + exchange_servicer, + ): + buy_price_level = exchange_pb.Level( + p="1000000000000000000", + q="1000000000000000", + ) + sell_price_level = exchange_pb.Level( + p="2000000000000000000", + q="2000000000000000", + ) + exchange_servicer.derivative_orderbook_responses.append( + exchange_query_pb.QueryDerivativeOrderbookResponse( + buys_price_level=[buy_price_level], + sells_price_level=[sell_price_level], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orderbook = await api.fetch_derivative_orderbook( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + limit_cumulative_notional="1000000000000000000", + pagination=PaginationOption(limit=100), + ) + expected_orderbook = { + "buysPriceLevel": [ + { + "p": buy_price_level.p, + "q": buy_price_level.q, + } + ], + "sellsPriceLevel": [ + { + "p": sell_price_level.p, + "q": sell_price_level.q, + } + ], + } + + assert orderbook == expected_orderbook + + @pytest.mark.asyncio + async def test_fetch_trader_derivative_orders( + self, + exchange_servicer, + ): + order = exchange_query_pb.TrimmedDerivativeLimitOrder( + price="1000000000000000000", + quantity="1000000000000000", + margin="1000000000000000000000000000000000", + fillable="1000000000000000", + isBuy=True, + order_hash="0x14e43adbb3302db28bcd0619068227ebca880cdd66cdfc8b4a662bcac0777849", + ) + exchange_servicer.trader_derivative_orders_responses.append( + exchange_query_pb.QueryTraderDerivativeOrdersResponse( + orders=[order], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orders = await api.fetch_trader_derivative_orders( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + ) + expected_orders = { + "orders": [ + { + "price": order.price, + "quantity": order.quantity, + "margin": order.margin, + "fillable": order.fillable, + "isBuy": order.isBuy, + "orderHash": order.order_hash, + } + ] + } + + assert orders == expected_orders + + @pytest.mark.asyncio + async def test_fetch_account_address_derivative_orders( + self, + exchange_servicer, + ): + order = exchange_query_pb.TrimmedDerivativeLimitOrder( + price="1000000000000000000", + quantity="1000000000000000", + margin="1000000000000000000000000000000000", + fillable="1000000000000000", + isBuy=True, + order_hash="0x14e43adbb3302db28bcd0619068227ebca880cdd66cdfc8b4a662bcac0777849", + ) + exchange_servicer.account_address_derivative_orders_responses.append( + exchange_query_pb.QueryAccountAddressDerivativeOrdersResponse( + orders=[order], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orders = await api.fetch_account_address_derivative_orders( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + account_address="inj1ady3s7whq30l4fx8sj3x6muv5mx4dfdlcpv8n7", + ) + expected_orders = { + "orders": [ + { + "price": order.price, + "quantity": order.quantity, + "margin": order.margin, + "fillable": order.fillable, + "isBuy": order.isBuy, + "orderHash": order.order_hash, + } + ] + } + + assert orders == expected_orders + + @pytest.mark.asyncio + async def test_fetch_derivative_orders_by_hashes( + self, + exchange_servicer, + ): + order = exchange_query_pb.TrimmedDerivativeLimitOrder( + price="1000000000000000000", + quantity="1000000000000000", + margin="1000000000000000000000000000000000", + fillable="1000000000000000", + isBuy=True, + order_hash="0x14e43adbb3302db28bcd0619068227ebca880cdd66cdfc8b4a662bcac0777849", + ) + exchange_servicer.derivative_orders_by_hashes_responses.append( + exchange_query_pb.QueryDerivativeOrdersByHashesResponse( + orders=[order], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orders = await api.fetch_derivative_orders_by_hashes( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + order_hashes=[order.order_hash], + ) + expected_orders = { + "orders": [ + { + "price": order.price, + "quantity": order.quantity, + "margin": order.margin, + "fillable": order.fillable, + "isBuy": order.isBuy, + "orderHash": order.order_hash, + } + ] + } + + assert orders == expected_orders + + @pytest.mark.asyncio + async def test_fetch_trader_derivative_transient_orders( + self, + exchange_servicer, + ): + order = exchange_query_pb.TrimmedDerivativeLimitOrder( + price="1000000000000000000", + quantity="1000000000000000", + margin="1000000000000000000000000000000000", + fillable="1000000000000000", + isBuy=True, + order_hash="0x14e43adbb3302db28bcd0619068227ebca880cdd66cdfc8b4a662bcac0777849", + ) + exchange_servicer.trader_derivative_transient_orders_responses.append( + exchange_query_pb.QueryTraderDerivativeOrdersResponse( + orders=[order], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orders = await api.fetch_trader_derivative_transient_orders( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + ) + expected_orders = { + "orders": [ + { + "price": order.price, + "quantity": order.quantity, + "margin": order.margin, + "fillable": order.fillable, + "isBuy": order.isBuy, + "orderHash": order.order_hash, + } + ] + } + + assert orders == expected_orders + + @pytest.mark.asyncio + async def test_fetch_derivative_markets( + self, + exchange_servicer, + ): + market = exchange_pb.DerivativeMarket( + ticker="20250608/USDT", + oracle_base="0x2d9315a88f3019f8efa88dfe9c0f0843712da0bac814461e27733f6b83eb51b3", + oracle_quote="0x1fc18861232290221461220bd4e2acd1dcdfbc89c84092c93c18bdc7756c1588", + oracle_type=9, + oracle_scale_factor=6, + quote_denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5", + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + initial_margin_ratio="50000000000000000", + maintenance_margin_ratio="20000000000000000", + maker_fee_rate="-0.0001", + taker_fee_rate="0.001", + relayer_fee_share_rate="400000000000000000", + isPerpetual=True, + status=1, + min_price_tick_size="100000000000000000000", + min_quantity_tick_size="1000000000000000", + ) + market_info = exchange_pb.PerpetualMarketInfo( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + hourly_funding_rate_cap="625000000000000", + hourly_interest_rate="4166660000000", + next_funding_timestamp=1708099200, + funding_interval=3600, + ) + funding_info = exchange_pb.PerpetualMarketFunding( + cumulative_funding="-107853477278881692857461", + cumulative_price="0", + last_timestamp=1708099200, + ) + perpetual_info = exchange_query_pb.PerpetualMarketState( + market_info=market_info, + funding_info=funding_info, + ) + mid_price_and_tob = exchange_pb.MidPriceAndTOB( + mid_price="2000000000000000000", + best_buy_price="1000000000000000000", + best_sell_price="3000000000000000000", + ) + full_market = exchange_query_pb.FullDerivativeMarket( + market=market, + perpetual_info=perpetual_info, + mark_price="33803835513327368963000000", + mid_price_and_tob=mid_price_and_tob, + ) + exchange_servicer.derivative_markets_responses.append( + exchange_query_pb.QueryDerivativeMarketsResponse( + markets=[full_market], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + status_string = exchange_pb.MarketStatus.Name(market.status) + markets = await api.fetch_derivative_markets( + status=status_string, + market_ids=[market.market_id], + ) + expected_markets = { + "markets": [ + { + "market": { + "ticker": market.ticker, + "oracleBase": market.oracle_base, + "oracleQuote": market.oracle_quote, + "oracleType": oracle_pb.OracleType.Name(market.oracle_type), + "oracleScaleFactor": market.oracle_scale_factor, + "quoteDenom": market.quote_denom, + "marketId": market.market_id, + "initialMarginRatio": market.initial_margin_ratio, + "maintenanceMarginRatio": market.maintenance_margin_ratio, + "makerFeeRate": market.maker_fee_rate, + "takerFeeRate": market.taker_fee_rate, + "relayerFeeShareRate": market.relayer_fee_share_rate, + "isPerpetual": market.isPerpetual, + "status": status_string, + "minPriceTickSize": market.min_price_tick_size, + "minQuantityTickSize": market.min_quantity_tick_size, + }, + "perpetualInfo": { + "marketInfo": { + "marketId": market_info.market_id, + "hourlyFundingRateCap": market_info.hourly_funding_rate_cap, + "hourlyInterestRate": market_info.hourly_interest_rate, + "nextFundingTimestamp": str(market_info.next_funding_timestamp), + "fundingInterval": str(market_info.funding_interval), + }, + "fundingInfo": { + "cumulativeFunding": funding_info.cumulative_funding, + "cumulativePrice": funding_info.cumulative_price, + "lastTimestamp": str(funding_info.last_timestamp), + }, + }, + "markPrice": full_market.mark_price, + "midPriceAndTob": { + "midPrice": mid_price_and_tob.mid_price, + "bestBuyPrice": mid_price_and_tob.best_buy_price, + "bestSellPrice": mid_price_and_tob.best_sell_price, + }, + } + ] + } + + assert markets == expected_markets + + @pytest.mark.asyncio + async def test_fetch_derivative_market( + self, + exchange_servicer, + ): + market = exchange_pb.DerivativeMarket( + ticker="INJ/USDT PERP", + oracle_base="0x2d9315a88f3019f8efa88dfe9c0f0843712da0bac814461e27733f6b83eb51b3", + oracle_quote="0x1fc18861232290221461220bd4e2acd1dcdfbc89c84092c93c18bdc7756c1588", + oracle_type=9, + oracle_scale_factor=6, + quote_denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5", + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + initial_margin_ratio="50000000000000000", + maintenance_margin_ratio="20000000000000000", + maker_fee_rate="-0.0001", + taker_fee_rate="0.001", + relayer_fee_share_rate="400000000000000000", + isPerpetual=True, + status=1, + min_price_tick_size="100000000000000000000", + min_quantity_tick_size="1000000000000000", + ) + market_info = exchange_pb.PerpetualMarketInfo( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + hourly_funding_rate_cap="625000000000000", + hourly_interest_rate="4166660000000", + next_funding_timestamp=1708099200, + funding_interval=3600, + ) + funding_info = exchange_pb.PerpetualMarketFunding( + cumulative_funding="-107853477278881692857461", + cumulative_price="0", + last_timestamp=1708099200, + ) + perpetual_info = exchange_query_pb.PerpetualMarketState( + market_info=market_info, + funding_info=funding_info, + ) + mid_price_and_tob = exchange_pb.MidPriceAndTOB( + mid_price="2000000000000000000", + best_buy_price="1000000000000000000", + best_sell_price="3000000000000000000", + ) + full_market = exchange_query_pb.FullDerivativeMarket( + market=market, + perpetual_info=perpetual_info, + mark_price="33803835513327368963000000", + mid_price_and_tob=mid_price_and_tob, + ) + exchange_servicer.derivative_market_responses.append( + exchange_query_pb.QueryDerivativeMarketResponse( + market=full_market, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + status_string = exchange_pb.MarketStatus.Name(market.status) + market_response = await api.fetch_derivative_market( + market_id=market.market_id, + ) + expected_market = { + "market": { + "market": { + "ticker": market.ticker, + "oracleBase": market.oracle_base, + "oracleQuote": market.oracle_quote, + "oracleType": oracle_pb.OracleType.Name(market.oracle_type), + "oracleScaleFactor": market.oracle_scale_factor, + "quoteDenom": market.quote_denom, + "marketId": market.market_id, + "initialMarginRatio": market.initial_margin_ratio, + "maintenanceMarginRatio": market.maintenance_margin_ratio, + "makerFeeRate": market.maker_fee_rate, + "takerFeeRate": market.taker_fee_rate, + "relayerFeeShareRate": market.relayer_fee_share_rate, + "isPerpetual": market.isPerpetual, + "status": status_string, + "minPriceTickSize": market.min_price_tick_size, + "minQuantityTickSize": market.min_quantity_tick_size, + }, + "perpetualInfo": { + "marketInfo": { + "marketId": market_info.market_id, + "hourlyFundingRateCap": market_info.hourly_funding_rate_cap, + "hourlyInterestRate": market_info.hourly_interest_rate, + "nextFundingTimestamp": str(market_info.next_funding_timestamp), + "fundingInterval": str(market_info.funding_interval), + }, + "fundingInfo": { + "cumulativeFunding": funding_info.cumulative_funding, + "cumulativePrice": funding_info.cumulative_price, + "lastTimestamp": str(funding_info.last_timestamp), + }, + }, + "markPrice": full_market.mark_price, + "midPriceAndTob": { + "midPrice": mid_price_and_tob.mid_price, + "bestBuyPrice": mid_price_and_tob.best_buy_price, + "bestSellPrice": mid_price_and_tob.best_sell_price, + }, + } + } + + assert market_response == expected_market + + @pytest.mark.asyncio + async def test_fetch_derivative_market_address( + self, + exchange_servicer, + ): + response = exchange_query_pb.QueryDerivativeMarketAddressResponse( + address="inj1zlh5sqevkfphtwnu9cul8p89vseme2eqt0snn9", + subaccount_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20000000000000000000000000", + ) + exchange_servicer.derivative_market_address_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + address = await api.fetch_derivative_market_address( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + expected_address = { + "address": response.address, + "subaccountId": response.subaccount_id, + } + + assert address == expected_address + + @pytest.mark.asyncio + async def test_fetch_subaccount_trade_nonce( + self, + exchange_servicer, + ): + response = exchange_query_pb.QuerySubaccountTradeNonceResponse(nonce=1234567879) + exchange_servicer.subaccount_trade_nonce_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + nonce = await api.fetch_subaccount_trade_nonce( + subaccount_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20000000000000000000000000", + ) + expected_nonce = { + "nonce": response.nonce, + } + + assert nonce == expected_nonce + + @pytest.mark.asyncio + async def test_fetch_positions( + self, + exchange_servicer, + ): + position = exchange_pb.Position( + isLong=True, + quantity="1000000000000000", + entry_price="2000000000000000000", + margin="2000000000000000000000000000000000", + cumulative_funding_entry="4000000", + ) + derivative_position = genesis_pb.DerivativePosition( + subaccount_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20000000000000000000000000", + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + position=position, + ) + exchange_servicer.positions_responses.append( + exchange_query_pb.QueryPositionsResponse(state=[derivative_position]) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + positions = await api.fetch_positions() + expected_positions = { + "state": [ + { + "subaccountId": derivative_position.subaccount_id, + "marketId": derivative_position.market_id, + "position": { + "isLong": position.isLong, + "quantity": position.quantity, + "entryPrice": position.entry_price, + "margin": position.margin, + "cumulativeFundingEntry": position.cumulative_funding_entry, + }, + }, + ], + } + + assert positions == expected_positions + + @pytest.mark.asyncio + async def test_fetch_subaccount_positions( + self, + exchange_servicer, + ): + position = exchange_pb.Position( + isLong=True, + quantity="1000000000000000", + entry_price="2000000000000000000", + margin="2000000000000000000000000000000000", + cumulative_funding_entry="4000000", + ) + derivative_position = genesis_pb.DerivativePosition( + subaccount_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20000000000000000000000000", + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + position=position, + ) + exchange_servicer.subaccount_positions_responses.append( + exchange_query_pb.QuerySubaccountPositionsResponse(state=[derivative_position]) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + positions = await api.fetch_subaccount_positions(subaccount_id=derivative_position.subaccount_id) + expected_positions = { + "state": [ + { + "subaccountId": derivative_position.subaccount_id, + "marketId": derivative_position.market_id, + "position": { + "isLong": position.isLong, + "quantity": position.quantity, + "entryPrice": position.entry_price, + "margin": position.margin, + "cumulativeFundingEntry": position.cumulative_funding_entry, + }, + }, + ], + } + + assert positions == expected_positions + + @pytest.mark.asyncio + async def test_fetch_subaccount_position_in_market( + self, + exchange_servicer, + ): + subaccount_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20000000000000000000000000" + market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6" + position = exchange_pb.Position( + isLong=True, + quantity="1000000000000000", + entry_price="2000000000000000000", + margin="2000000000000000000000000000000000", + cumulative_funding_entry="4000000", + ) + exchange_servicer.subaccount_position_in_market_responses.append( + exchange_query_pb.QuerySubaccountPositionInMarketResponse(state=position) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + position_response = await api.fetch_subaccount_position_in_market( + subaccount_id=subaccount_id, + market_id=market_id, + ) + expected_position = { + "state": { + "isLong": position.isLong, + "quantity": position.quantity, + "entryPrice": position.entry_price, + "margin": position.margin, + "cumulativeFundingEntry": position.cumulative_funding_entry, + }, + } + + assert position_response == expected_position + + @pytest.mark.asyncio + async def test_fetch_subaccount_effective_position_in_market( + self, + exchange_servicer, + ): + subaccount_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20000000000000000000000000" + market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6" + + effective_position = exchange_query_pb.EffectivePosition( + is_long=True, + quantity="1000000000000000", + entry_price="2000000000000000000", + effective_margin="2000000000000000000000000000000000", + ) + exchange_servicer.subaccount_effective_position_in_market_responses.append( + exchange_query_pb.QuerySubaccountEffectivePositionInMarketResponse(state=effective_position) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + position_response = await api.fetch_subaccount_effective_position_in_market( + subaccount_id=subaccount_id, + market_id=market_id, + ) + expected_position = { + "state": { + "isLong": effective_position.is_long, + "quantity": effective_position.quantity, + "entryPrice": effective_position.entry_price, + "effectiveMargin": effective_position.effective_margin, + }, + } + + assert position_response == expected_position + + @pytest.mark.asyncio + async def test_fetch_perpetual_market_info( + self, + exchange_servicer, + ): + perpetual_market_info = exchange_pb.PerpetualMarketInfo( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + hourly_funding_rate_cap="625000000000000", + hourly_interest_rate="4166660000000", + next_funding_timestamp=1708099200, + funding_interval=3600, + ) + exchange_servicer.perpetual_market_info_responses.append( + exchange_query_pb.QueryPerpetualMarketInfoResponse(info=perpetual_market_info) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + market_info = await api.fetch_perpetual_market_info(market_id=perpetual_market_info.market_id) + expected_market_info = { + "info": { + "marketId": perpetual_market_info.market_id, + "hourlyFundingRateCap": perpetual_market_info.hourly_funding_rate_cap, + "hourlyInterestRate": perpetual_market_info.hourly_interest_rate, + "nextFundingTimestamp": str(perpetual_market_info.next_funding_timestamp), + "fundingInterval": str(perpetual_market_info.funding_interval), + } + } + + assert market_info == expected_market_info + + @pytest.mark.asyncio + async def test_fetch_expiry_futures_market_info( + self, + exchange_servicer, + ): + expiry_futures_market_info = exchange_pb.ExpiryFuturesMarketInfo( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + expiration_timestamp=1708099200, + twap_start_timestamp=1705566200, + expiration_twap_start_price_cumulative="1000000000000000000", + settlement_price="2000000000000000000", + ) + exchange_servicer.expiry_futures_market_info_responses.append( + exchange_query_pb.QueryExpiryFuturesMarketInfoResponse(info=expiry_futures_market_info) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + market_info = await api.fetch_expiry_futures_market_info(market_id=expiry_futures_market_info.market_id) + expected_market_info = { + "info": { + "marketId": expiry_futures_market_info.market_id, + "expirationTimestamp": str(expiry_futures_market_info.expiration_timestamp), + "twapStartTimestamp": str(expiry_futures_market_info.twap_start_timestamp), + "expirationTwapStartPriceCumulative": expiry_futures_market_info.expiration_twap_start_price_cumulative, + "settlementPrice": expiry_futures_market_info.settlement_price, + } + } + + assert market_info == expected_market_info + + @pytest.mark.asyncio + async def test_fetch_perpetual_market_funding( + self, + exchange_servicer, + ): + perpetual_market_funding = exchange_pb.PerpetualMarketFunding( + cumulative_funding="-107853477278881692857461", + cumulative_price="0", + last_timestamp=1708099200, + ) + exchange_servicer.perpetual_market_funding_responses.append( + exchange_query_pb.QueryPerpetualMarketFundingResponse(state=perpetual_market_funding) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + funding = await api.fetch_perpetual_market_funding( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6" + ) + expected_funding = { + "state": { + "cumulativeFunding": perpetual_market_funding.cumulative_funding, + "cumulativePrice": perpetual_market_funding.cumulative_price, + "lastTimestamp": str(perpetual_market_funding.last_timestamp), + } + } + + assert funding == expected_funding + + @pytest.mark.asyncio + async def test_fetch_subaccount_order_metadata( + self, + exchange_servicer, + ): + metadata = exchange_pb.SubaccountOrderbookMetadata( + vanilla_limit_order_count=1, + reduce_only_limit_order_count=2, + aggregate_reduce_only_quantity="1000000000000000", + aggregate_vanilla_quantity="2000000000000000", + vanilla_conditional_order_count=3, + reduce_only_conditional_order_count=4, + ) + subaccount_order_metadata = exchange_query_pb.SubaccountOrderbookMetadataWithMarket( + metadata=metadata, + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + isBuy=True, + ) + exchange_servicer.subaccount_order_metadata_responses.append( + exchange_query_pb.QuerySubaccountOrderMetadataResponse(metadata=[subaccount_order_metadata]) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + metadata_response = await api.fetch_subaccount_order_metadata( + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001" + ) + expected_metadata = { + "metadata": [ + { + "metadata": { + "vanillaLimitOrderCount": metadata.vanilla_limit_order_count, + "reduceOnlyLimitOrderCount": metadata.reduce_only_limit_order_count, + "aggregateReduceOnlyQuantity": metadata.aggregate_reduce_only_quantity, + "aggregateVanillaQuantity": metadata.aggregate_vanilla_quantity, + "vanillaConditionalOrderCount": metadata.vanilla_conditional_order_count, + "reduceOnlyConditionalOrderCount": metadata.reduce_only_conditional_order_count, + }, + "marketId": subaccount_order_metadata.market_id, + "isBuy": subaccount_order_metadata.isBuy, + }, + ] + } + + assert metadata_response == expected_metadata + + @pytest.mark.asyncio + async def test_fetch_trade_reward_points( + self, + exchange_servicer, + ): + points = "40" + response = exchange_query_pb.QueryTradeRewardPointsResponse(account_trade_reward_points=[points]) + exchange_servicer.trade_reward_points_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + trade_reward_points = await api.fetch_trade_reward_points( + accounts=["inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r"], + pending_pool_timestamp=1708099200, + ) + expected_trade_reward_points = {"accountTradeRewardPoints": [points]} + + assert trade_reward_points == expected_trade_reward_points + + @pytest.mark.asyncio + async def test_fetch_pending_trade_reward_points( + self, + exchange_servicer, + ): + points = "40" + response = exchange_query_pb.QueryTradeRewardPointsResponse(account_trade_reward_points=[points]) + exchange_servicer.pending_trade_reward_points_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + trade_reward_points = await api.fetch_pending_trade_reward_points( + accounts=["inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r"], + pending_pool_timestamp=1708099200, + ) + expected_trade_reward_points = {"accountTradeRewardPoints": [points]} + + assert trade_reward_points == expected_trade_reward_points + + @pytest.mark.asyncio + async def test_fetch_fee_discount_account_info( + self, + exchange_servicer, + ): + account_info = exchange_pb.FeeDiscountTierInfo( + maker_discount_rate="0.0001", + taker_discount_rate="0.0002", + staked_amount="1000000000", + volume="1000000000000000000", + ) + account_ttl = exchange_pb.FeeDiscountTierTTL( + tier=3, + ttl_timestamp=1708099200, + ) + response = exchange_query_pb.QueryFeeDiscountAccountInfoResponse( + tier_level=3, + account_info=account_info, + account_ttl=account_ttl, + ) + exchange_servicer.fee_discount_account_info_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + fee_discount = await api.fetch_fee_discount_account_info(account="inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r") + expected_fee_discount = { + "tierLevel": str(response.tier_level), + "accountInfo": { + "makerDiscountRate": account_info.maker_discount_rate, + "takerDiscountRate": account_info.taker_discount_rate, + "stakedAmount": account_info.staked_amount, + "volume": account_info.volume, + }, + "accountTtl": { + "tier": str(account_ttl.tier), + "ttlTimestamp": str(account_ttl.ttl_timestamp), + }, + } + + assert fee_discount == expected_fee_discount + + @pytest.mark.asyncio + async def test_fetch_fee_discount_schedule( + self, + exchange_servicer, + ): + fee_discount_tier_info = exchange_pb.FeeDiscountTierInfo( + maker_discount_rate="0.0001", + taker_discount_rate="0.0002", + staked_amount="1000000000", + volume="1000000000000000000", + ) + fee_discount_schedule = exchange_pb.FeeDiscountSchedule( + bucket_count=3, + bucket_duration=3600, + quote_denoms=["peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"], + tier_infos=[fee_discount_tier_info], + disqualified_market_ids=["0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"], + ) + exchange_servicer.fee_discount_schedule_responses.append( + exchange_query_pb.QueryFeeDiscountScheduleResponse( + fee_discount_schedule=fee_discount_schedule, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + schedule = await api.fetch_fee_discount_schedule() + expected_schedule = { + "feeDiscountSchedule": { + "bucketCount": str(fee_discount_schedule.bucket_count), + "bucketDuration": str(fee_discount_schedule.bucket_duration), + "quoteDenoms": fee_discount_schedule.quote_denoms, + "tierInfos": [ + { + "makerDiscountRate": fee_discount_tier_info.maker_discount_rate, + "takerDiscountRate": fee_discount_tier_info.taker_discount_rate, + "stakedAmount": fee_discount_tier_info.staked_amount, + "volume": fee_discount_tier_info.volume, + } + ], + "disqualifiedMarketIds": fee_discount_schedule.disqualified_market_ids, + }, + } + + assert schedule == expected_schedule + + @pytest.mark.asyncio + async def test_fetch_balance_mismatches( + self, + exchange_servicer, + ): + balance_mismatch = exchange_query_pb.BalanceMismatch( + subaccountId="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5", + available="1000000000000000", + total="2000000000000000", + balance_hold="3000000000000000", + expected_total="4000000000000000", + difference="500000000000000", + ) + exchange_servicer.balance_mismatches_responses.append( + exchange_query_pb.QueryBalanceMismatchesResponse( + balance_mismatches=[balance_mismatch], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + mismatches = await api.fetch_balance_mismatches(dust_factor=20) + expected_mismatches = { + "balanceMismatches": [ + { + "subaccountId": balance_mismatch.subaccountId, + "denom": balance_mismatch.denom, + "available": balance_mismatch.available, + "total": balance_mismatch.total, + "balanceHold": balance_mismatch.balance_hold, + "expectedTotal": balance_mismatch.expected_total, + "difference": balance_mismatch.difference, + } + ], + } + + assert mismatches == expected_mismatches + + @pytest.mark.asyncio + async def test_fetch_balance_with_balance_holds( + self, + exchange_servicer, + ): + balance_with_balance_hold = exchange_query_pb.BalanceWithMarginHold( + subaccountId="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5", + available="1000000000000000", + total="2000000000000000", + balance_hold="3000000000000000", + ) + exchange_servicer.balance_with_balance_holds_responses.append( + exchange_query_pb.QueryBalanceWithBalanceHoldsResponse( + balance_with_balance_holds=[balance_with_balance_hold], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + balance = await api.fetch_balance_with_balance_holds() + expected_balance = { + "balanceWithBalanceHolds": [ + { + "subaccountId": balance_with_balance_hold.subaccountId, + "denom": balance_with_balance_hold.denom, + "available": balance_with_balance_hold.available, + "total": balance_with_balance_hold.total, + "balanceHold": balance_with_balance_hold.balance_hold, + } + ], + } + + assert balance == expected_balance + + @pytest.mark.asyncio + async def test_fetch_fee_discount_tier_statistics( + self, + exchange_servicer, + ): + tier_statistics = exchange_query_pb.TierStatistic( + tier=3, + count=30, + ) + exchange_servicer.fee_discount_tier_statistics_responses.append( + exchange_query_pb.QueryFeeDiscountTierStatisticsResponse( + statistics=[tier_statistics], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + statistics = await api.fetch_fee_discount_tier_statistics() + expected_statistics = { + "statistics": [ + { + "tier": str(tier_statistics.tier), + "count": str(tier_statistics.count), + } + ], + } + + assert statistics == expected_statistics + + @pytest.mark.asyncio + async def test_fetch_mito_vault_infos( + self, + exchange_servicer, + ): + master_address = "inj1zlh5sqevkfphtwnu9cul8p89vseme2eqt0snn9" + derivative_address = "inj1zlh5" + spot_address = "inj1zlh6" + cw20_address = "inj1zlh7" + response = exchange_query_pb.MitoVaultInfosResponse( + master_addresses=[master_address], + derivative_addresses=[derivative_address], + spot_addresses=[spot_address], + cw20_addresses=[cw20_address], + ) + exchange_servicer.mito_vault_infos_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + mito_vaults = await api.fetch_mito_vault_infos() + expected_mito_vaults = { + "masterAddresses": [master_address], + "derivativeAddresses": [derivative_address], + "spotAddresses": [spot_address], + "cw20Addresses": [cw20_address], + } + + assert mito_vaults == expected_mito_vaults + + @pytest.mark.asyncio + async def test_fetch_market_id_from_vault( + self, + exchange_servicer, + ): + market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6" + exchange_servicer.market_id_from_vault_responses.append( + exchange_query_pb.QueryMarketIDFromVaultResponse( + market_id=market_id, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + market_id_response = await api.fetch_market_id_from_vault( + vault_address="inj1zlh5sqevkfphtwnu9cul8p89vseme2eqt0snn9" + ) + expected_market_id = { + "marketId": market_id, + } + + assert market_id_response == expected_market_id + + @pytest.mark.asyncio + async def test_fetch_historical_trade_records( + self, + exchange_servicer, + ): + latest_trade_record = exchange_pb.TradeRecord( + timestamp=1708099200, + price="2000000000000000000", + quantity="1000000000000000", + ) + trade_record = exchange_pb.TradeRecords( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + latest_trade_records=[latest_trade_record], + ) + exchange_servicer.historical_trade_records_responses.append( + exchange_query_pb.QueryHistoricalTradeRecordsResponse( + trade_records=[trade_record], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + records = await api.fetch_historical_trade_records(market_id=trade_record.market_id) + expected_records = { + "tradeRecords": [ + { + "marketId": trade_record.market_id, + "latestTradeRecords": [ + { + "timestamp": str(latest_trade_record.timestamp), + "price": latest_trade_record.price, + "quantity": latest_trade_record.quantity, + } + ], + }, + ], + } + + assert records == expected_records + + @pytest.mark.asyncio + async def test_fetch_is_opted_out_of_rewards( + self, + exchange_servicer, + ): + response = exchange_query_pb.QueryIsOptedOutOfRewardsResponse( + is_opted_out=False, + ) + exchange_servicer.is_opted_out_of_rewards_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + is_opted_out = await api.fetch_is_opted_out_of_rewards(account="inj1zlh5sqevkfphtwnu9cul8p89vseme2eqt0snn9") + expected_is_opted_out = { + "isOptedOut": response.is_opted_out, + } + + assert is_opted_out == expected_is_opted_out + + @pytest.mark.asyncio + async def test_fetch_opted_out_of_rewards_accounts( + self, + exchange_servicer, + ): + response = exchange_query_pb.QueryOptedOutOfRewardsAccountsResponse( + accounts=["inj1zlh5sqevkfphtwnu9cul8p89vseme2eqt0snn9"], + ) + exchange_servicer.opted_out_of_rewards_accounts_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + opted_out = await api.fetch_opted_out_of_rewards_accounts() + expected_opted_out = { + "accounts": response.accounts, + } + + assert opted_out == expected_opted_out + + @pytest.mark.asyncio + async def test_fetch_market_volatility( + self, + exchange_servicer, + ): + history_metadata = oracle_pb.MetadataStatistics( + group_count=2, + records_sample_size=10, + mean="0.0001", + twap="0.0005", + first_timestamp=1702399200, + last_timestamp=1708099200, + min_price="1000000000000", + max_price="3000000000000", + median_price="2000000000000", + ) + trade_record = exchange_pb.TradeRecord( + timestamp=1708099200, + price="2000000000000000000", + quantity="1000000000000000", + ) + response = exchange_query_pb.QueryMarketVolatilityResponse( + volatility="0.0001", history_metadata=history_metadata, raw_history=[trade_record] + ) + exchange_servicer.market_volatility_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + volatility = await api.fetch_market_volatility( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + trade_grouping_sec=0, + max_age=28000000, + include_raw_history=True, + include_metadata=True, + ) + expected_volatility = { + "volatility": response.volatility, + "historyMetadata": { + "groupCount": history_metadata.group_count, + "recordsSampleSize": history_metadata.records_sample_size, + "mean": history_metadata.mean, + "twap": history_metadata.twap, + "firstTimestamp": str(history_metadata.first_timestamp), + "lastTimestamp": str(history_metadata.last_timestamp), + "minPrice": history_metadata.min_price, + "maxPrice": history_metadata.max_price, + "medianPrice": history_metadata.median_price, + }, + "rawHistory": [ + { + "timestamp": str(trade_record.timestamp), + "price": trade_record.price, + "quantity": trade_record.quantity, + } + ], + } + + assert volatility == expected_volatility + + @pytest.mark.asyncio + async def test_fetch_binary_options_markets( + self, + exchange_servicer, + ): + market = exchange_pb.BinaryOptionsMarket( + ticker="20250608/USDT", + oracle_symbol="0x2d9315a88f3019f8efa88dfe9c0f0843712da0bac814461e27733f6b83eb51b3", + oracle_provider="Pyth", + oracle_type=9, + oracle_scale_factor=6, + expiration_timestamp=1708099200, + settlement_timestamp=1707099200, + admin="inj1zlh5sqevkfphtwnu9cul8p89vseme2eqt0snn9", + quote_denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5", + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + maker_fee_rate="-0.0001", + taker_fee_rate="0.001", + relayer_fee_share_rate="400000000000000000", + status=1, + min_price_tick_size="100000000000000000000", + min_quantity_tick_size="1000000000000000", + settlement_price="2000000000000000000", + ) + response = exchange_query_pb.QueryBinaryMarketsResponse( + markets=[market], + ) + exchange_servicer.binary_options_markets_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + markets = await api.fetch_binary_options_markets(status="Active") + expected_markets = { + "markets": [ + { + "ticker": market.ticker, + "oracleSymbol": market.oracle_symbol, + "oracleProvider": market.oracle_provider, + "oracleType": oracle_pb.OracleType.Name(market.oracle_type), + "oracleScaleFactor": market.oracle_scale_factor, + "expirationTimestamp": str(market.expiration_timestamp), + "settlementTimestamp": str(market.settlement_timestamp), + "admin": market.admin, + "quoteDenom": market.quote_denom, + "marketId": market.market_id, + "makerFeeRate": market.maker_fee_rate, + "takerFeeRate": market.taker_fee_rate, + "relayerFeeShareRate": market.relayer_fee_share_rate, + "status": exchange_pb.MarketStatus.Name(market.status), + "minPriceTickSize": market.min_price_tick_size, + "minQuantityTickSize": market.min_quantity_tick_size, + "settlementPrice": market.settlement_price, + }, + ] + } + + assert markets == expected_markets + + @pytest.mark.asyncio + async def test_fetch_trader_derivative_conditional_orders( + self, + exchange_servicer, + ): + order = exchange_query_pb.TrimmedDerivativeConditionalOrder( + price="2000000000000000000", + quantity="1000000000000000", + margin="2000000000000000000000000000000000", + triggerPrice="3000000000000000000", + isBuy=True, + isLimit=True, + order_hash="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + response = exchange_query_pb.QueryTraderDerivativeConditionalOrdersResponse(orders=[order]) + exchange_servicer.trader_derivative_conditional_orders_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + orders = await api.fetch_trader_derivative_conditional_orders( + subaccount_id="0x5303d92e49a619bb29de8fb6f59c0e7589213cc8000000000000000000000001", + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + expected_orders = { + "orders": [ + { + "price": order.price, + "quantity": order.quantity, + "margin": order.margin, + "triggerPrice": order.triggerPrice, + "isBuy": order.isBuy, + "isLimit": order.isLimit, + "orderHash": order.order_hash, + } + ] + } + + assert orders == expected_orders + + @pytest.mark.asyncio + async def test_fetch_market_atomic_execution_fee_multiplier( + self, + exchange_servicer, + ): + response = exchange_query_pb.QueryMarketAtomicExecutionFeeMultiplierResponse( + multiplier="100", + ) + exchange_servicer.market_atomic_execution_fee_multiplier_responses.append(response) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcExchangeApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = exchange_servicer + + multiplier = await api.fetch_market_atomic_execution_fee_multiplier( + market_id="0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6", + ) + expected_multiplier = { + "multiplier": response.multiplier, + } + + assert multiplier == expected_multiplier + + async def _dummy_metadata_provider(self): + return None