From 33e29622943f0ed87f6b3dde3aca0f9c690ea8b8 Mon Sep 17 00:00:00 2001 From: abel Date: Fri, 9 Feb 2024 00:14:58 -0300 Subject: [PATCH 1/3] (feat) Added support for all `distribution` module queries. Added also new example scripts for all queries and unit tests --- .../1_ValidatorDistributionInfo.py | 16 + .../2_ValidatorOutstandingRewards.py | 16 + .../distribution/3_ValidatorCommission.py | 16 + .../distribution/4_ValidatorSlashes.py | 20 ++ .../distribution/5_DelegationRewards.py | 19 + .../distribution/6_DelegationTotalRewards.py | 18 + .../distribution/7_DelegatorValidators.py | 18 + .../8_DelegatorWithdrawAddress.py | 18 + .../distribution/9_CommunityPool.py | 15 + pyinjective/async_client.py | 67 ++++ .../chain/grpc/chain_grpc_distribution_api.py | 109 ++++++ ...y => configurable_authz_query_servicer.py} | 0 ...onfigurable_distribution_query_servicer.py | 69 ++++ .../grpc/test_chain_grpc_distribution_api.py | 338 ++++++++++++++++++ 14 files changed, 739 insertions(+) create mode 100644 examples/chain_client/distribution/1_ValidatorDistributionInfo.py create mode 100644 examples/chain_client/distribution/2_ValidatorOutstandingRewards.py create mode 100644 examples/chain_client/distribution/3_ValidatorCommission.py create mode 100644 examples/chain_client/distribution/4_ValidatorSlashes.py create mode 100644 examples/chain_client/distribution/5_DelegationRewards.py create mode 100644 examples/chain_client/distribution/6_DelegationTotalRewards.py create mode 100644 examples/chain_client/distribution/7_DelegatorValidators.py create mode 100644 examples/chain_client/distribution/8_DelegatorWithdrawAddress.py create mode 100644 examples/chain_client/distribution/9_CommunityPool.py create mode 100644 pyinjective/client/chain/grpc/chain_grpc_distribution_api.py rename tests/client/chain/grpc/{configurable_autz_query_servicer.py => configurable_authz_query_servicer.py} (100%) create mode 100644 tests/client/chain/grpc/configurable_distribution_query_servicer.py create mode 100644 tests/client/chain/grpc/test_chain_grpc_distribution_api.py diff --git a/examples/chain_client/distribution/1_ValidatorDistributionInfo.py b/examples/chain_client/distribution/1_ValidatorDistributionInfo.py new file mode 100644 index 00000000..13161630 --- /dev/null +++ b/examples/chain_client/distribution/1_ValidatorDistributionInfo.py @@ -0,0 +1,16 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + network = Network.testnet() + client = AsyncClient(network) + validator_address = "injvaloper1jue5dpr9lerjn6wlwtrywxrsenrf28ru89z99z" + distribution_info = await client.fetch_validator_distribution_info(validator_address=validator_address) + print(distribution_info) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/2_ValidatorOutstandingRewards.py b/examples/chain_client/distribution/2_ValidatorOutstandingRewards.py new file mode 100644 index 00000000..31833682 --- /dev/null +++ b/examples/chain_client/distribution/2_ValidatorOutstandingRewards.py @@ -0,0 +1,16 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + network = Network.testnet() + client = AsyncClient(network) + validator_address = "injvaloper1jue5dpr9lerjn6wlwtrywxrsenrf28ru89z99z" + rewards = await client.fetch_validator_outstanding_rewards(validator_address=validator_address) + print(rewards) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/3_ValidatorCommission.py b/examples/chain_client/distribution/3_ValidatorCommission.py new file mode 100644 index 00000000..a82e191b --- /dev/null +++ b/examples/chain_client/distribution/3_ValidatorCommission.py @@ -0,0 +1,16 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + network = Network.testnet() + client = AsyncClient(network) + validator_address = "injvaloper1jue5dpr9lerjn6wlwtrywxrsenrf28ru89z99z" + commission = await client.fetch_validator_commission(validator_address=validator_address) + print(commission) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/4_ValidatorSlashes.py b/examples/chain_client/distribution/4_ValidatorSlashes.py new file mode 100644 index 00000000..08dbfea1 --- /dev/null +++ b/examples/chain_client/distribution/4_ValidatorSlashes.py @@ -0,0 +1,20 @@ +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() + client = AsyncClient(network) + limit = 2 + pagination = PaginationOption(limit=limit) + validator_address = "injvaloper1jue5dpr9lerjn6wlwtrywxrsenrf28ru89z99z" + contracts = await client.fetch_validator_slashes(validator_address=validator_address, pagination=pagination) + print(contracts) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/5_DelegationRewards.py b/examples/chain_client/distribution/5_DelegationRewards.py new file mode 100644 index 00000000..da112263 --- /dev/null +++ b/examples/chain_client/distribution/5_DelegationRewards.py @@ -0,0 +1,19 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + network = Network.testnet() + client = AsyncClient(network) + delegator_address = "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r" + validator_address = "injvaloper156t3yxd4udv0h9gwagfcmwnmm3quy0nph7tyh5" + rewards = await client.fetch_delegation_rewards( + delegator_address=delegator_address, validator_address=validator_address + ) + print(rewards) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/6_DelegationTotalRewards.py b/examples/chain_client/distribution/6_DelegationTotalRewards.py new file mode 100644 index 00000000..c67dc94f --- /dev/null +++ b/examples/chain_client/distribution/6_DelegationTotalRewards.py @@ -0,0 +1,18 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + network = Network.testnet() + client = AsyncClient(network) + delegator_address = "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r" + rewards = await client.fetch_delegation_total_rewards( + delegator_address=delegator_address, + ) + print(rewards) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/7_DelegatorValidators.py b/examples/chain_client/distribution/7_DelegatorValidators.py new file mode 100644 index 00000000..f03fb8ec --- /dev/null +++ b/examples/chain_client/distribution/7_DelegatorValidators.py @@ -0,0 +1,18 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + network = Network.testnet() + client = AsyncClient(network) + delegator_address = "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r" + validators = await client.fetch_delegator_validators( + delegator_address=delegator_address, + ) + print(validators) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/8_DelegatorWithdrawAddress.py b/examples/chain_client/distribution/8_DelegatorWithdrawAddress.py new file mode 100644 index 00000000..d3c27091 --- /dev/null +++ b/examples/chain_client/distribution/8_DelegatorWithdrawAddress.py @@ -0,0 +1,18 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + network = Network.testnet() + client = AsyncClient(network) + delegator_address = "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r" + withdraw_address = await client.fetch_delegator_withdraw_address( + delegator_address=delegator_address, + ) + print(withdraw_address) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/9_CommunityPool.py b/examples/chain_client/distribution/9_CommunityPool.py new file mode 100644 index 00000000..7a803d03 --- /dev/null +++ b/examples/chain_client/distribution/9_CommunityPool.py @@ -0,0 +1,15 @@ +import asyncio + +from pyinjective.async_client import AsyncClient +from pyinjective.core.network import Network + + +async def main() -> None: + network = Network.testnet() + client = AsyncClient(network) + community_pool = await client.fetch_community_pool() + print(community_pool) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/pyinjective/async_client.py b/pyinjective/async_client.py index e8da821b..dea12f12 100644 --- a/pyinjective/async_client.py +++ b/pyinjective/async_client.py @@ -12,6 +12,7 @@ from pyinjective.client.chain.grpc.chain_grpc_auth_api import ChainGrpcAuthApi 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_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 @@ -178,6 +179,12 @@ def __init__( metadata_query_provider=self._chain_cookie_metadata_requestor ), ) + self.distribution_api = ChainGrpcDistributionApi( + 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( @@ -572,6 +579,66 @@ async def fetch_send_enabled( ) -> Dict[str, Any]: return await self.bank_api.fetch_send_enabled(denoms=denoms, pagination=pagination) + async def fetch_validator_distribution_info(self, validator_address: str) -> Dict[str, Any]: + return await self.distribution_api.fetch_validator_distribution_info(validator_address=validator_address) + + async def fetch_validator_outstanding_rewards(self, validator_address: str) -> Dict[str, Any]: + return await self.distribution_api.fetch_validator_outstanding_rewards(validator_address=validator_address) + + async def fetch_validator_commission(self, validator_address: str) -> Dict[str, Any]: + return await self.distribution_api.fetch_validator_commission(validator_address=validator_address) + + async def fetch_validator_slashes( + self, + validator_address: str, + starting_height: Optional[int] = None, + ending_height: Optional[int] = None, + pagination: Optional[PaginationOption] = None, + ) -> Dict[str, Any]: + return await self.distribution_api.fetch_validator_slashes( + validator_address=validator_address, + starting_height=starting_height, + ending_height=ending_height, + pagination=pagination, + ) + + async def fetch_delegation_rewards( + self, + delegator_address: str, + validator_address: str, + ) -> Dict[str, Any]: + return await self.distribution_api.fetch_delegation_rewards( + delegator_address=delegator_address, + validator_address=validator_address, + ) + + async def fetch_delegation_total_rewards( + self, + delegator_address: str, + ) -> Dict[str, Any]: + return await self.distribution_api.fetch_delegation_total_rewards( + delegator_address=delegator_address, + ) + + async def fetch_delegator_validators( + self, + delegator_address: str, + ) -> Dict[str, Any]: + return await self.distribution_api.fetch_delegator_validators( + delegator_address=delegator_address, + ) + + async def fetch_delegator_withdraw_address( + self, + delegator_address: str, + ) -> Dict[str, Any]: + return await self.distribution_api.fetch_delegator_withdraw_address( + delegator_address=delegator_address, + ) + + async def fetch_community_pool(self) -> Dict[str, Any]: + return await self.distribution_api.fetch_community_pool() + # Injective Exchange client methods # Auction RPC diff --git a/pyinjective/client/chain/grpc/chain_grpc_distribution_api.py b/pyinjective/client/chain/grpc/chain_grpc_distribution_api.py new file mode 100644 index 00000000..e7da0966 --- /dev/null +++ b/pyinjective/client/chain/grpc/chain_grpc_distribution_api.py @@ -0,0 +1,109 @@ +from typing import Any, Callable, Dict, Optional + +from grpc.aio import Channel + +from pyinjective.client.model.pagination import PaginationOption +from pyinjective.proto.cosmos.distribution.v1beta1 import ( + query_pb2 as distribution_query_pb, + query_pb2_grpc as distribution_query_grpc, +) +from pyinjective.utils.grpc_api_request_assistant import GrpcApiRequestAssistant + + +class ChainGrpcDistributionApi: + def __init__(self, channel: Channel, metadata_provider: Callable): + self._stub = distribution_query_grpc.QueryStub(channel) + self._assistant = GrpcApiRequestAssistant(metadata_provider=metadata_provider) + + async def fetch_module_params(self) -> Dict[str, Any]: + request = distribution_query_pb.QueryParamsRequest() + response = await self._execute_call(call=self._stub.Params, request=request) + + return response + + async def fetch_validator_distribution_info(self, validator_address: str) -> Dict[str, Any]: + request = distribution_query_pb.QueryValidatorDistributionInfoRequest(validator_address=validator_address) + response = await self._execute_call(call=self._stub.ValidatorDistributionInfo, request=request) + + return response + + async def fetch_validator_outstanding_rewards(self, validator_address: str) -> Dict[str, Any]: + request = distribution_query_pb.QueryValidatorOutstandingRewardsRequest(validator_address=validator_address) + response = await self._execute_call(call=self._stub.ValidatorOutstandingRewards, request=request) + + return response + + async def fetch_validator_commission(self, validator_address: str) -> Dict[str, Any]: + request = distribution_query_pb.QueryValidatorCommissionRequest(validator_address=validator_address) + response = await self._execute_call(call=self._stub.ValidatorCommission, request=request) + + return response + + async def fetch_validator_slashes( + self, + validator_address: str, + starting_height: Optional[int] = None, + ending_height: Optional[int] = None, + pagination: Optional[PaginationOption] = None, + ) -> Dict[str, Any]: + pagination_request = None + if pagination is not None: + pagination_request = pagination.create_pagination_request() + request = distribution_query_pb.QueryValidatorSlashesRequest( + validator_address=validator_address, + starting_height=starting_height, + ending_height=ending_height, + pagination=pagination_request, + ) + response = await self._execute_call(call=self._stub.ValidatorSlashes, request=request) + + return response + + async def fetch_delegation_rewards( + self, + delegator_address: str, + validator_address: str, + ) -> Dict[str, Any]: + request = distribution_query_pb.QueryDelegationRewardsRequest( + delegator_address=delegator_address, + validator_address=validator_address, + ) + response = await self._execute_call(call=self._stub.DelegationRewards, request=request) + + return response + + async def fetch_delegation_total_rewards( + self, + delegator_address: str, + ) -> Dict[str, Any]: + request = distribution_query_pb.QueryDelegationTotalRewardsRequest( + delegator_address=delegator_address, + ) + response = await self._execute_call(call=self._stub.DelegationTotalRewards, request=request) + + return response + + async def fetch_delegator_validators(self, delegator_address: str) -> Dict[str, Any]: + request = distribution_query_pb.QueryDelegatorValidatorsRequest( + delegator_address=delegator_address, + ) + response = await self._execute_call(call=self._stub.DelegatorValidators, request=request) + + return response + + async def fetch_delegator_withdraw_address(self, delegator_address: str) -> Dict[str, Any]: + request = distribution_query_pb.QueryDelegatorWithdrawAddressRequest( + delegator_address=delegator_address, + ) + response = await self._execute_call(call=self._stub.DelegatorWithdrawAddress, request=request) + + return response + + async def fetch_community_pool(self) -> Dict[str, Any]: + request = distribution_query_pb.QueryCommunityPoolRequest() + response = await self._execute_call(call=self._stub.CommunityPool, 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_autz_query_servicer.py b/tests/client/chain/grpc/configurable_authz_query_servicer.py similarity index 100% rename from tests/client/chain/grpc/configurable_autz_query_servicer.py rename to tests/client/chain/grpc/configurable_authz_query_servicer.py diff --git a/tests/client/chain/grpc/configurable_distribution_query_servicer.py b/tests/client/chain/grpc/configurable_distribution_query_servicer.py new file mode 100644 index 00000000..d2e1d7da --- /dev/null +++ b/tests/client/chain/grpc/configurable_distribution_query_servicer.py @@ -0,0 +1,69 @@ +from collections import deque + +from pyinjective.proto.cosmos.distribution.v1beta1 import ( + query_pb2 as distribution_query_pb, + query_pb2_grpc as distribution_query_grpc, +) + + +class ConfigurableDistributionQueryServicer(distribution_query_grpc.QueryServicer): + def __init__(self): + super().__init__() + self.distribution_params = deque() + self.validator_distribution_info_responses = deque() + self.validator_outstanding_rewards_responses = deque() + self.validator_commission_responses = deque() + self.validator_slashes_responses = deque() + self.delegation_rewards_responses = deque() + self.delegation_total_rewards_responses = deque() + self.delegator_validators_responses = deque() + self.delegator_withdraw_address_responses = deque() + self.community_pool_responses = deque() + + async def Params(self, request: distribution_query_pb.QueryParamsRequest, context=None, metadata=None): + return self.distribution_params.pop() + + async def ValidatorDistributionInfo( + self, request: distribution_query_pb.QueryValidatorDistributionInfoRequest, context=None, metadata=None + ): + return self.validator_distribution_info_responses.pop() + + async def ValidatorOutstandingRewards( + self, request: distribution_query_pb.QueryValidatorOutstandingRewardsRequest, context=None, metadata=None + ): + return self.validator_outstanding_rewards_responses.pop() + + async def ValidatorCommission( + self, request: distribution_query_pb.QueryValidatorCommissionRequest, context=None, metadata=None + ): + return self.validator_commission_responses.pop() + + async def ValidatorSlashes( + self, request: distribution_query_pb.QueryValidatorSlashesRequest, context=None, metadata=None + ): + return self.validator_slashes_responses.pop() + + async def DelegationRewards( + self, request: distribution_query_pb.QueryDelegationRewardsRequest, context=None, metadata=None + ): + return self.delegation_rewards_responses.pop() + + async def DelegationTotalRewards( + self, request: distribution_query_pb.QueryDelegationTotalRewardsRequest, context=None, metadata=None + ): + return self.delegation_total_rewards_responses.pop() + + async def DelegatorValidators( + self, request: distribution_query_pb.QueryDelegatorValidatorsRequest, context=None, metadata=None + ): + return self.delegator_validators_responses.pop() + + async def DelegatorWithdrawAddress( + self, request: distribution_query_pb.QueryDelegatorWithdrawAddressRequest, context=None, metadata=None + ): + return self.delegator_withdraw_address_responses.pop() + + async def CommunityPool( + self, request: distribution_query_pb.QueryCommunityPoolRequest, context=None, metadata=None + ): + return self.community_pool_responses.pop() diff --git a/tests/client/chain/grpc/test_chain_grpc_distribution_api.py b/tests/client/chain/grpc/test_chain_grpc_distribution_api.py new file mode 100644 index 00000000..9f8ee9a6 --- /dev/null +++ b/tests/client/chain/grpc/test_chain_grpc_distribution_api.py @@ -0,0 +1,338 @@ +import base64 + +import grpc +import pytest + +from pyinjective.client.chain.grpc.chain_grpc_distribution_api import ChainGrpcDistributionApi +from pyinjective.client.model.pagination import PaginationOption +from pyinjective.core.network import Network +from pyinjective.proto.cosmos.base.query.v1beta1 import pagination_pb2 as pagination_pb +from pyinjective.proto.cosmos.base.v1beta1 import coin_pb2 as coin_pb +from pyinjective.proto.cosmos.distribution.v1beta1 import ( + distribution_pb2 as distribution_pb, + query_pb2 as distribution_query_pb, +) +from tests.client.chain.grpc.configurable_distribution_query_servicer import ConfigurableDistributionQueryServicer + + +@pytest.fixture +def distribution_servicer(): + return ConfigurableDistributionQueryServicer() + + +class TestChainGrpcAuthApi: + @pytest.mark.asyncio + async def test_fetch_module_params( + self, + distribution_servicer, + ): + params = distribution_pb.Params( + community_tax="0.050000000000000000", + base_proposer_reward="0.060000000000000000", + bonus_proposer_reward="0.070000000000000000", + withdraw_addr_enabled=True, + ) + + distribution_servicer.distribution_params.append(distribution_query_pb.QueryParamsResponse(params=params)) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcDistributionApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = distribution_servicer + + module_params = await api.fetch_module_params() + expected_params = { + "params": { + "communityTax": params.community_tax, + "baseProposerReward": params.base_proposer_reward, + "bonusProposerReward": params.bonus_proposer_reward, + "withdrawAddrEnabled": params.withdraw_addr_enabled, + } + } + + assert expected_params == module_params + + @pytest.mark.asyncio + async def test_fetch_validator_distribution_info( + self, + distribution_servicer, + ): + operator = "inj1zpy3qf7us3m0pxpqkp72gzjv55t70huyxh7slz" + reward = coin_pb.DecCoin(denom="inj", amount="1000000000") + commission = coin_pb.DecCoin(denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5", amount="54497408") + + distribution_servicer.validator_distribution_info_responses.append( + distribution_query_pb.QueryValidatorDistributionInfoResponse( + operator_address=operator, self_bond_rewards=[reward], commission=[commission] + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcDistributionApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = distribution_servicer + + validator_info = await api.fetch_validator_distribution_info(validator_address=operator) + expected_info = { + "operatorAddress": operator, + "selfBondRewards": [{"denom": reward.denom, "amount": reward.amount}], + "commission": [{"denom": commission.denom, "amount": commission.amount}], + } + + assert expected_info == validator_info + + @pytest.mark.asyncio + async def test_fetch_validator_outstanding_rewards( + self, + distribution_servicer, + ): + operator = "inj1zpy3qf7us3m0pxpqkp72gzjv55t70huyxh7slz" + reward = coin_pb.DecCoin(denom="inj", amount="1000000000") + rewards = distribution_pb.ValidatorOutstandingRewards(rewards=[reward]) + + distribution_servicer.validator_outstanding_rewards_responses.append( + distribution_query_pb.QueryValidatorOutstandingRewardsResponse( + rewards=rewards, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcDistributionApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = distribution_servicer + + validator_rewards = await api.fetch_validator_outstanding_rewards(validator_address=operator) + expected_rewards = {"rewards": {"rewards": [{"denom": reward.denom, "amount": reward.amount}]}} + + assert expected_rewards == validator_rewards + + @pytest.mark.asyncio + async def test_fetch_validator_commission( + self, + distribution_servicer, + ): + operator = "inj1zpy3qf7us3m0pxpqkp72gzjv55t70huyxh7slz" + first_commission = coin_pb.DecCoin(denom="inj", amount="1000000000") + commission = distribution_pb.ValidatorAccumulatedCommission(commission=[first_commission]) + + distribution_servicer.validator_commission_responses.append( + distribution_query_pb.QueryValidatorCommissionResponse( + commission=commission, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcDistributionApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = distribution_servicer + + commission = await api.fetch_validator_commission(validator_address=operator) + expected_commission = { + "commission": {"commission": [{"denom": first_commission.denom, "amount": first_commission.amount}]} + } + + assert expected_commission == commission + + @pytest.mark.asyncio + async def test_fetch_validator_slashes( + self, + distribution_servicer, + ): + operator = "inj1zpy3qf7us3m0pxpqkp72gzjv55t70huyxh7slz" + slash = distribution_pb.ValidatorSlashEvent( + validator_period=1, + fraction="4", + ) + pagination = pagination_pb.PageResponse(total=2) + + distribution_servicer.validator_slashes_responses.append( + distribution_query_pb.QueryValidatorSlashesResponse( + slashes=[slash], + pagination=pagination, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcDistributionApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = distribution_servicer + + slashes = await api.fetch_validator_slashes( + validator_address=operator, + starting_height=20, + ending_height=100, + pagination=PaginationOption( + skip=0, + limit=100, + ), + ) + expected_slashes = { + "slashes": [ + { + "validatorPeriod": str(slash.validator_period), + "fraction": slash.fraction, + } + ], + "pagination": {"nextKey": base64.b64encode(pagination.next_key).decode(), "total": str(pagination.total)}, + } + + assert slashes == expected_slashes + + @pytest.mark.asyncio + async def test_fetch_delegation_rewards( + self, + distribution_servicer, + ): + delegator = "inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r" + validator = "injvaloper16gdnrnl224ylje5z9vd0vn0msym7p58f00qauj" + reward = coin_pb.DecCoin(denom="inj", amount="1000000000") + + distribution_servicer.delegation_rewards_responses.append( + distribution_query_pb.QueryDelegationRewardsResponse( + rewards=[reward], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcDistributionApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = distribution_servicer + + rewards = await api.fetch_delegation_rewards( + delegator_address=delegator, + validator_address=validator, + ) + expected_rewards = {"rewards": [{"denom": reward.denom, "amount": reward.amount}]} + + assert rewards == expected_rewards + + @pytest.mark.asyncio + async def test_fetch_delegation_total_rewards( + self, + distribution_servicer, + ): + delegator = "inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r" + validator = "injvaloper16gdnrnl224ylje5z9vd0vn0msym7p58f00qauj" + reward = coin_pb.DecCoin(denom="inj", amount="1000000000") + delegation_delegator_reward = distribution_pb.DelegationDelegatorReward( + validator_address=validator, + reward=[reward], + ) + total = coin_pb.DecCoin(denom="inj", amount="2000000000") + + distribution_servicer.delegation_total_rewards_responses.append( + distribution_query_pb.QueryDelegationTotalRewardsResponse( + rewards=[delegation_delegator_reward], + total=[total], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcDistributionApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = distribution_servicer + + rewards = await api.fetch_delegation_total_rewards( + delegator_address=delegator, + ) + expected_rewards = { + "rewards": [ + { + "validatorAddress": validator, + "reward": [{"denom": reward.denom, "amount": reward.amount}], + } + ], + "total": [{"denom": total.denom, "amount": total.amount}], + } + + assert rewards == expected_rewards + + @pytest.mark.asyncio + async def test_fetch_delegator_validators( + self, + distribution_servicer, + ): + delegator = "inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r" + validator = "injvaloper16gdnrnl224ylje5z9vd0vn0msym7p58f00qauj" + + distribution_servicer.delegator_validators_responses.append( + distribution_query_pb.QueryDelegatorValidatorsResponse( + validators=[validator], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcDistributionApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = distribution_servicer + + validators = await api.fetch_delegator_validators( + delegator_address=delegator, + ) + expected_validators = { + "validators": [validator], + } + + assert validators == expected_validators + + @pytest.mark.asyncio + async def test_fetch_delegator_withdraw_address( + self, + distribution_servicer, + ): + delegator = "inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r" + + distribution_servicer.delegator_withdraw_address_responses.append( + distribution_query_pb.QueryDelegatorWithdrawAddressResponse( + withdraw_address=delegator, + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcDistributionApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = distribution_servicer + + withdraw_address = await api.fetch_delegator_withdraw_address( + delegator_address=delegator, + ) + expected_withdraw_address = { + "withdrawAddress": delegator, + } + + assert withdraw_address == expected_withdraw_address + + @pytest.mark.asyncio + async def test_fetch_community_pool( + self, + distribution_servicer, + ): + coin = coin_pb.DecCoin(denom="inj", amount="1000000000") + distribution_servicer.community_pool_responses.append( + distribution_query_pb.QueryCommunityPoolResponse( + pool=[coin], + ) + ) + + network = Network.devnet() + channel = grpc.aio.insecure_channel(network.grpc_endpoint) + + api = ChainGrpcDistributionApi(channel=channel, metadata_provider=lambda: self._dummy_metadata_provider()) + api._stub = distribution_servicer + + community_pool = await api.fetch_community_pool() + expected_community_pool = {"pool": [{"denom": coin.denom, "amount": coin.amount}]} + + assert community_pool == expected_community_pool + + async def _dummy_metadata_provider(self): + return None From 51a73f40028ff32ba49848b254cbde1c661f81c9 Mon Sep 17 00:00:00 2001 From: abel Date: Fri, 9 Feb 2024 00:16:59 -0300 Subject: [PATCH 2/3] (fix) Fix error due to incomplete rename --- tests/client/chain/grpc/test_chain_grpc_authz_api.py | 2 +- tests/test_async_client_deprecation_warnings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/client/chain/grpc/test_chain_grpc_authz_api.py b/tests/client/chain/grpc/test_chain_grpc_authz_api.py index e097ff66..9d1d5586 100644 --- a/tests/client/chain/grpc/test_chain_grpc_authz_api.py +++ b/tests/client/chain/grpc/test_chain_grpc_authz_api.py @@ -7,7 +7,7 @@ from pyinjective.core.network import Network from pyinjective.proto.cosmos.authz.v1beta1 import authz_pb2, query_pb2 as authz_query from pyinjective.proto.cosmos.base.query.v1beta1 import pagination_pb2 as pagination_pb -from tests.client.chain.grpc.configurable_autz_query_servicer import ConfigurableAuthZQueryServicer +from tests.client.chain.grpc.configurable_authz_query_servicer import ConfigurableAuthZQueryServicer @pytest.fixture diff --git a/tests/test_async_client_deprecation_warnings.py b/tests/test_async_client_deprecation_warnings.py index dae2d807..1df77fe8 100644 --- a/tests/test_async_client_deprecation_warnings.py +++ b/tests/test_async_client_deprecation_warnings.py @@ -21,7 +21,7 @@ from pyinjective.proto.injective.stream.v1beta1 import query_pb2 as chain_stream_pb from pyinjective.proto.injective.types.v1beta1 import account_pb2 as account_pb from tests.client.chain.grpc.configurable_auth_query_servicer import ConfigurableAuthQueryServicer -from tests.client.chain.grpc.configurable_autz_query_servicer import ConfigurableAuthZQueryServicer +from tests.client.chain.grpc.configurable_authz_query_servicer import ConfigurableAuthZQueryServicer from tests.client.chain.grpc.configurable_bank_query_servicer import ConfigurableBankQueryServicer from tests.client.chain.stream_grpc.configurable_chain_stream_query_servicer import ConfigurableChainStreamQueryServicer from tests.client.indexer.configurable_account_query_servicer import ConfigurableAccountQueryServicer From 03b81d612333d6f3adcacc08e2838549cfbd6fed Mon Sep 17 00:00:00 2001 From: abel Date: Mon, 12 Feb 2024 15:44:17 -0300 Subject: [PATCH 3/3] (feat) Added support in Composer to create all missing messages from the `distribution` module. Marked as deprecated old functions not following Python naming standards and created the replacements for them (with the correct name following standards) --- examples/chain_client/0_LocalOrderHash.py | 6 +- .../13_MsgIncreasePositionMargin.py | 2 +- examples/chain_client/15_MsgWithdraw.py | 2 +- .../chain_client/16_MsgSubaccountTransfer.py | 2 +- .../chain_client/17_MsgBatchUpdateOrders.py | 2 +- examples/chain_client/18_MsgBid.py | 2 +- examples/chain_client/19_MsgGrant.py | 2 +- examples/chain_client/1_MsgSend.py | 2 +- examples/chain_client/20_MsgExec.py | 2 +- examples/chain_client/21_MsgRevoke.py | 2 +- examples/chain_client/22_MsgSendToEth.py | 2 +- .../chain_client/23_MsgRelayPriceFeedPrice.py | 2 +- examples/chain_client/24_MsgRewardsOptOut.py | 2 +- examples/chain_client/25_MsgDelegate.py | 2 +- .../26_MsgWithdrawDelegatorReward.py | 81 ----------- examples/chain_client/2_MsgDeposit.py | 2 +- examples/chain_client/30_ExternalTransfer.py | 2 +- .../31_MsgCreateBinaryOptionsLimitOrder.py | 2 +- .../32_MsgCreateBinaryOptionsMarketOrder.py | 2 +- .../33_MsgCancelBinaryOptionsOrder.py | 2 +- .../34_MsgAdminUpdateBinaryOptionsMarket.py | 2 +- .../35_MsgInstantBinaryOptionsMarketLaunch.py | 2 +- .../chain_client/36_MsgRelayProviderPrices.py | 2 +- .../chain_client/3_MsgCreateSpotLimitOrder.py | 2 +- .../chain_client/40_MsgExecuteContract.py | 8 +- .../chain_client/41_MsgCreateInsuranceFund.py | 2 +- examples/chain_client/42_MsgUnderwrite.py | 2 +- .../chain_client/43_MsgRequestRedemption.py | 2 +- ...8_WithdrawValidatorCommissionAndRewards.py | 86 ------------ .../4_MsgCreateSpotMarketOrder.py | 2 +- examples/chain_client/5_MsgCancelSpotOrder.py | 2 +- .../6_MsgCreateDerivativeLimitOrder.py | 2 +- examples/chain_client/72_MsgMint.py | 2 +- examples/chain_client/73_MsgBurn.py | 2 +- .../76_MsgExecuteContractCompat.py | 2 +- .../chain_client/77_MsgLiquidatePosition.py | 2 +- .../7_MsgCreateDerivativeMarketOrder.py | 2 +- .../8_MsgCancelDerivativeOrder.py | 2 +- .../distribution/10_SetWithdrawAddress.py | 46 ++++++ .../11_WithdrawDelegatorReward.py | 47 +++++++ .../12_WithdrawValidatorCommission.py | 45 ++++++ .../distribution/13_FundCommunityPool.py | 47 +++++++ pyinjective/composer.py | 132 +++++++++++++----- pyinjective/core/broadcaster.py | 4 +- tests/test_composer.py | 4 +- 45 files changed, 325 insertions(+), 249 deletions(-) delete mode 100644 examples/chain_client/26_MsgWithdrawDelegatorReward.py delete mode 100644 examples/chain_client/48_WithdrawValidatorCommissionAndRewards.py create mode 100644 examples/chain_client/distribution/10_SetWithdrawAddress.py create mode 100644 examples/chain_client/distribution/11_WithdrawDelegatorReward.py create mode 100644 examples/chain_client/distribution/12_WithdrawValidatorCommission.py create mode 100644 examples/chain_client/distribution/13_FundCommunityPool.py diff --git a/examples/chain_client/0_LocalOrderHash.py b/examples/chain_client/0_LocalOrderHash.py index a25398c6..770ad5bf 100644 --- a/examples/chain_client/0_LocalOrderHash.py +++ b/examples/chain_client/0_LocalOrderHash.py @@ -113,7 +113,7 @@ async def main() -> None: gas_limit = base_gas + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) @@ -150,7 +150,7 @@ async def main() -> None: gas_limit = base_gas + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) @@ -240,7 +240,7 @@ async def main() -> None: gas_limit = base_gas + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/13_MsgIncreasePositionMargin.py b/examples/chain_client/13_MsgIncreasePositionMargin.py index f54910f8..775a41e5 100644 --- a/examples/chain_client/13_MsgIncreasePositionMargin.py +++ b/examples/chain_client/13_MsgIncreasePositionMargin.py @@ -66,7 +66,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/15_MsgWithdraw.py b/examples/chain_client/15_MsgWithdraw.py index a8c267d8..b198c0aa 100644 --- a/examples/chain_client/15_MsgWithdraw.py +++ b/examples/chain_client/15_MsgWithdraw.py @@ -57,7 +57,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/16_MsgSubaccountTransfer.py b/examples/chain_client/16_MsgSubaccountTransfer.py index d87eb0ed..b68717b2 100644 --- a/examples/chain_client/16_MsgSubaccountTransfer.py +++ b/examples/chain_client/16_MsgSubaccountTransfer.py @@ -64,7 +64,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/17_MsgBatchUpdateOrders.py b/examples/chain_client/17_MsgBatchUpdateOrders.py index 557ffe46..9efdc0a0 100644 --- a/examples/chain_client/17_MsgBatchUpdateOrders.py +++ b/examples/chain_client/17_MsgBatchUpdateOrders.py @@ -153,7 +153,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/18_MsgBid.py b/examples/chain_client/18_MsgBid.py index 8e5f8d5e..1efaa3e3 100644 --- a/examples/chain_client/18_MsgBid.py +++ b/examples/chain_client/18_MsgBid.py @@ -56,7 +56,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/19_MsgGrant.py b/examples/chain_client/19_MsgGrant.py index 32a82fe7..111a050c 100644 --- a/examples/chain_client/19_MsgGrant.py +++ b/examples/chain_client/19_MsgGrant.py @@ -76,7 +76,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/1_MsgSend.py b/examples/chain_client/1_MsgSend.py index 6389c8be..1d926898 100644 --- a/examples/chain_client/1_MsgSend.py +++ b/examples/chain_client/1_MsgSend.py @@ -61,7 +61,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/20_MsgExec.py b/examples/chain_client/20_MsgExec.py index d5ff2dc5..1203ff90 100644 --- a/examples/chain_client/20_MsgExec.py +++ b/examples/chain_client/20_MsgExec.py @@ -83,7 +83,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/21_MsgRevoke.py b/examples/chain_client/21_MsgRevoke.py index 82663004..91bb192d 100644 --- a/examples/chain_client/21_MsgRevoke.py +++ b/examples/chain_client/21_MsgRevoke.py @@ -61,7 +61,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/22_MsgSendToEth.py b/examples/chain_client/22_MsgSendToEth.py index 59bdc877..06e2d78c 100644 --- a/examples/chain_client/22_MsgSendToEth.py +++ b/examples/chain_client/22_MsgSendToEth.py @@ -70,7 +70,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/23_MsgRelayPriceFeedPrice.py b/examples/chain_client/23_MsgRelayPriceFeedPrice.py index cc7055c9..b59b34a3 100644 --- a/examples/chain_client/23_MsgRelayPriceFeedPrice.py +++ b/examples/chain_client/23_MsgRelayPriceFeedPrice.py @@ -61,7 +61,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/24_MsgRewardsOptOut.py b/examples/chain_client/24_MsgRewardsOptOut.py index 5fa2f429..8beaa1f4 100644 --- a/examples/chain_client/24_MsgRewardsOptOut.py +++ b/examples/chain_client/24_MsgRewardsOptOut.py @@ -56,7 +56,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/25_MsgDelegate.py b/examples/chain_client/25_MsgDelegate.py index ada14ce0..e32f6691 100644 --- a/examples/chain_client/25_MsgDelegate.py +++ b/examples/chain_client/25_MsgDelegate.py @@ -61,7 +61,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/26_MsgWithdrawDelegatorReward.py b/examples/chain_client/26_MsgWithdrawDelegatorReward.py deleted file mode 100644 index f9370c23..00000000 --- a/examples/chain_client/26_MsgWithdrawDelegatorReward.py +++ /dev/null @@ -1,81 +0,0 @@ -import asyncio -import os - -import dotenv -from grpc import RpcError - -from pyinjective.async_client import AsyncClient -from pyinjective.constant import GAS_FEE_BUFFER_AMOUNT, GAS_PRICE -from pyinjective.core.network import Network -from pyinjective.transaction import Transaction -from pyinjective.wallet import PrivateKey - - -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) - composer = await client.composer() - await client.sync_timeout_height() - - # 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()) - - # prepare tx msg - validator_address = "injvaloper1ultw9r29l8nxy5u6thcgusjn95vsy2caw722q5" - - msg = composer.MsgWithdrawDelegatorReward( - delegator_address=address.to_acc_bech32(), validator_address=validator_address - ) - - # build sim tx - tx = ( - Transaction() - .with_messages(msg) - .with_sequence(client.get_sequence()) - .with_account_num(client.get_number()) - .with_chain_id(network.chain_id) - ) - sim_sign_doc = tx.get_sign_doc(pub_key) - sim_sig = priv_key.sign(sim_sign_doc.SerializeToString()) - sim_tx_raw_bytes = tx.get_tx_data(sim_sig, pub_key) - - # simulate tx - try: - sim_res = await client.simulate(sim_tx_raw_bytes) - except RpcError as ex: - print(ex) - return - - # build tx - gas_price = GAS_PRICE - gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation - gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") - fee = [ - composer.Coin( - amount=gas_price * gas_limit, - denom=network.fee_denom, - ) - ] - tx = tx.with_gas(gas_limit).with_fee(fee).with_memo("").with_timeout_height(client.timeout_height) - sign_doc = tx.get_sign_doc(pub_key) - sig = priv_key.sign(sign_doc.SerializeToString()) - tx_raw_bytes = tx.get_tx_data(sig, pub_key) - - # broadcast tx: send_tx_async_mode, send_tx_sync_mode, send_tx_block_mode - res = await client.broadcast_tx_sync_mode(tx_raw_bytes) - print(res) - print("gas wanted: {}".format(gas_limit)) - print("gas fee: {} INJ".format(gas_fee)) - - -if __name__ == "__main__": - asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/2_MsgDeposit.py b/examples/chain_client/2_MsgDeposit.py index 80587c55..80b9f652 100644 --- a/examples/chain_client/2_MsgDeposit.py +++ b/examples/chain_client/2_MsgDeposit.py @@ -57,7 +57,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/30_ExternalTransfer.py b/examples/chain_client/30_ExternalTransfer.py index 7ab6e0f2..3200be34 100644 --- a/examples/chain_client/30_ExternalTransfer.py +++ b/examples/chain_client/30_ExternalTransfer.py @@ -64,7 +64,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/31_MsgCreateBinaryOptionsLimitOrder.py b/examples/chain_client/31_MsgCreateBinaryOptionsLimitOrder.py index 57f5537b..2a58e173 100644 --- a/examples/chain_client/31_MsgCreateBinaryOptionsLimitOrder.py +++ b/examples/chain_client/31_MsgCreateBinaryOptionsLimitOrder.py @@ -81,7 +81,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/32_MsgCreateBinaryOptionsMarketOrder.py b/examples/chain_client/32_MsgCreateBinaryOptionsMarketOrder.py index 473e8e53..2d07658f 100644 --- a/examples/chain_client/32_MsgCreateBinaryOptionsMarketOrder.py +++ b/examples/chain_client/32_MsgCreateBinaryOptionsMarketOrder.py @@ -75,7 +75,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/33_MsgCancelBinaryOptionsOrder.py b/examples/chain_client/33_MsgCancelBinaryOptionsOrder.py index 15159f00..82107f3c 100644 --- a/examples/chain_client/33_MsgCancelBinaryOptionsOrder.py +++ b/examples/chain_client/33_MsgCancelBinaryOptionsOrder.py @@ -66,7 +66,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/34_MsgAdminUpdateBinaryOptionsMarket.py b/examples/chain_client/34_MsgAdminUpdateBinaryOptionsMarket.py index 55c308be..0f39dcdb 100644 --- a/examples/chain_client/34_MsgAdminUpdateBinaryOptionsMarket.py +++ b/examples/chain_client/34_MsgAdminUpdateBinaryOptionsMarket.py @@ -74,7 +74,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/35_MsgInstantBinaryOptionsMarketLaunch.py b/examples/chain_client/35_MsgInstantBinaryOptionsMarketLaunch.py index 7e865320..7b8a6567 100644 --- a/examples/chain_client/35_MsgInstantBinaryOptionsMarketLaunch.py +++ b/examples/chain_client/35_MsgInstantBinaryOptionsMarketLaunch.py @@ -76,7 +76,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/36_MsgRelayProviderPrices.py b/examples/chain_client/36_MsgRelayProviderPrices.py index 4ee5fc3f..1e3b9c8f 100644 --- a/examples/chain_client/36_MsgRelayProviderPrices.py +++ b/examples/chain_client/36_MsgRelayProviderPrices.py @@ -66,7 +66,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/3_MsgCreateSpotLimitOrder.py b/examples/chain_client/3_MsgCreateSpotLimitOrder.py index dd06672a..d3ca5f16 100644 --- a/examples/chain_client/3_MsgCreateSpotLimitOrder.py +++ b/examples/chain_client/3_MsgCreateSpotLimitOrder.py @@ -77,7 +77,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/40_MsgExecuteContract.py b/examples/chain_client/40_MsgExecuteContract.py index f2d1b719..d18c6398 100644 --- a/examples/chain_client/40_MsgExecuteContract.py +++ b/examples/chain_client/40_MsgExecuteContract.py @@ -33,12 +33,12 @@ async def main() -> None: # prepare tx msg # NOTE: COIN MUST BE SORTED IN ALPHABETICAL ORDER BY DENOMS funds = [ - composer.Coin( + composer.coin( amount=69, denom="factory/inj1hdvy6tl89llqy3ze8lv6mz5qh66sx9enn0jxg6/inj12ngevx045zpvacus9s6anr258gkwpmthnz80e9", ), - composer.Coin(amount=420, denom="peggy0x44C21afAaF20c270EBbF5914Cfc3b5022173FEB7"), - composer.Coin(amount=1, denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"), + composer.coin(amount=420, denom="peggy0x44C21afAaF20c270EBbF5914Cfc3b5022173FEB7"), + composer.coin(amount=1, denom="peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"), ] msg = composer.MsgExecuteContract( sender=address.to_acc_bech32(), @@ -71,7 +71,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/41_MsgCreateInsuranceFund.py b/examples/chain_client/41_MsgCreateInsuranceFund.py index 74f94bfa..99c10fc0 100644 --- a/examples/chain_client/41_MsgCreateInsuranceFund.py +++ b/examples/chain_client/41_MsgCreateInsuranceFund.py @@ -65,7 +65,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/42_MsgUnderwrite.py b/examples/chain_client/42_MsgUnderwrite.py index 20c65a64..16b75e70 100644 --- a/examples/chain_client/42_MsgUnderwrite.py +++ b/examples/chain_client/42_MsgUnderwrite.py @@ -61,7 +61,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/43_MsgRequestRedemption.py b/examples/chain_client/43_MsgRequestRedemption.py index 4413044b..ecc2793c 100644 --- a/examples/chain_client/43_MsgRequestRedemption.py +++ b/examples/chain_client/43_MsgRequestRedemption.py @@ -61,7 +61,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/48_WithdrawValidatorCommissionAndRewards.py b/examples/chain_client/48_WithdrawValidatorCommissionAndRewards.py deleted file mode 100644 index 2f67f6c4..00000000 --- a/examples/chain_client/48_WithdrawValidatorCommissionAndRewards.py +++ /dev/null @@ -1,86 +0,0 @@ -import asyncio -import os - -import dotenv -from grpc import RpcError - -from pyinjective.async_client import AsyncClient -from pyinjective.composer import Composer as ProtoMsgComposer -from pyinjective.constant import GAS_FEE_BUFFER_AMOUNT, GAS_PRICE -from pyinjective.core.network import Network -from pyinjective.transaction import Transaction -from pyinjective.wallet import PrivateKey - - -async def main() -> None: - dotenv.load_dotenv() - configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") - - """For a validator to withdraw his rewards & commissions simultaneously""" - # select network: local, testnet, mainnet - network = Network.testnet() - composer = ProtoMsgComposer(network=network.string()) - - # initialize grpc client - client = AsyncClient(network, insecure=False) - await client.sync_timeout_height() - - # load account - # private key is that from the validator's wallet - 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()) - - # prepare tx msg - validator_address = "injvaloper1ultw9r29l8nxy5u6thcgusjn95vsy2caw722q5" - - msg0 = composer.MsgWithdrawDelegatorReward( - delegator_address=address.to_acc_bech32(), validator_address=validator_address - ) - - msg1 = composer.MsgWithdrawValidatorCommission(validator_address=validator_address) - - # build sim tx - tx = ( - Transaction() - .with_messages(msg0, msg1) - .with_sequence(client.get_sequence()) - .with_account_num(client.get_number()) - .with_chain_id(network.chain_id) - ) - sim_sign_doc = tx.get_sign_doc(pub_key) - sim_sig = priv_key.sign(sim_sign_doc.SerializeToString()) - sim_tx_raw_bytes = tx.get_tx_data(sim_sig, pub_key) - - # simulate tx - try: - sim_res = await client.simulate(sim_tx_raw_bytes) - except RpcError as ex: - print(ex) - return - - # build tx - gas_price = GAS_PRICE - gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation - gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") - fee = [ - composer.Coin( - amount=gas_price * gas_limit, - denom=network.fee_denom, - ) - ] - tx = tx.with_gas(gas_limit).with_fee(fee).with_memo("").with_timeout_height(client.timeout_height) - sign_doc = tx.get_sign_doc(pub_key) - sig = priv_key.sign(sign_doc.SerializeToString()) - tx_raw_bytes = tx.get_tx_data(sig, pub_key) - - # broadcast tx: send_tx_async_mode, send_tx_sync_mode, send_tx_block_mode - res = await client.broadcast_tx_sync_mode(tx_raw_bytes) - print(res) - print("gas wanted: {}".format(gas_limit)) - print("gas fee: {} INJ".format(gas_fee)) - - -if __name__ == "__main__": - asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/4_MsgCreateSpotMarketOrder.py b/examples/chain_client/4_MsgCreateSpotMarketOrder.py index 06a42a96..5229632f 100644 --- a/examples/chain_client/4_MsgCreateSpotMarketOrder.py +++ b/examples/chain_client/4_MsgCreateSpotMarketOrder.py @@ -75,7 +75,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/5_MsgCancelSpotOrder.py b/examples/chain_client/5_MsgCancelSpotOrder.py index 63167309..970b2190 100644 --- a/examples/chain_client/5_MsgCancelSpotOrder.py +++ b/examples/chain_client/5_MsgCancelSpotOrder.py @@ -63,7 +63,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/6_MsgCreateDerivativeLimitOrder.py b/examples/chain_client/6_MsgCreateDerivativeLimitOrder.py index e46f1acb..41a19307 100644 --- a/examples/chain_client/6_MsgCreateDerivativeLimitOrder.py +++ b/examples/chain_client/6_MsgCreateDerivativeLimitOrder.py @@ -77,7 +77,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/72_MsgMint.py b/examples/chain_client/72_MsgMint.py index d38449be..b6617431 100644 --- a/examples/chain_client/72_MsgMint.py +++ b/examples/chain_client/72_MsgMint.py @@ -26,7 +26,7 @@ async def main() -> None: pub_key = priv_key.to_public_key() address = pub_key.to_address() - amount = composer.Coin(amount=1_000_000_000, denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test") + amount = composer.coin(amount=1_000_000_000, denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test") message = composer.msg_mint( sender=address.to_acc_bech32(), diff --git a/examples/chain_client/73_MsgBurn.py b/examples/chain_client/73_MsgBurn.py index 441f4ee8..c4aa013a 100644 --- a/examples/chain_client/73_MsgBurn.py +++ b/examples/chain_client/73_MsgBurn.py @@ -26,7 +26,7 @@ async def main() -> None: pub_key = priv_key.to_public_key() address = pub_key.to_address() - amount = composer.Coin(amount=100, denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test") + amount = composer.coin(amount=100, denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test") message = composer.msg_burn( sender=address.to_acc_bech32(), diff --git a/examples/chain_client/76_MsgExecuteContractCompat.py b/examples/chain_client/76_MsgExecuteContractCompat.py index 892c125f..a195c7c4 100644 --- a/examples/chain_client/76_MsgExecuteContractCompat.py +++ b/examples/chain_client/76_MsgExecuteContractCompat.py @@ -68,7 +68,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/77_MsgLiquidatePosition.py b/examples/chain_client/77_MsgLiquidatePosition.py index 6dcbacfb..d80ba385 100644 --- a/examples/chain_client/77_MsgLiquidatePosition.py +++ b/examples/chain_client/77_MsgLiquidatePosition.py @@ -83,7 +83,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/7_MsgCreateDerivativeMarketOrder.py b/examples/chain_client/7_MsgCreateDerivativeMarketOrder.py index e7e06c62..37b305ed 100644 --- a/examples/chain_client/7_MsgCreateDerivativeMarketOrder.py +++ b/examples/chain_client/7_MsgCreateDerivativeMarketOrder.py @@ -76,7 +76,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/8_MsgCancelDerivativeOrder.py b/examples/chain_client/8_MsgCancelDerivativeOrder.py index 6a59167a..43180b1f 100644 --- a/examples/chain_client/8_MsgCancelDerivativeOrder.py +++ b/examples/chain_client/8_MsgCancelDerivativeOrder.py @@ -63,7 +63,7 @@ async def main() -> None: gas_limit = int(sim_res["gasInfo"]["gasUsed"]) + GAS_FEE_BUFFER_AMOUNT # add buffer for gas fee computation gas_fee = "{:.18f}".format((gas_price * gas_limit) / pow(10, 18)).rstrip("0") fee = [ - composer.Coin( + composer.coin( amount=gas_price * gas_limit, denom=network.fee_denom, ) diff --git a/examples/chain_client/distribution/10_SetWithdrawAddress.py b/examples/chain_client/distribution/10_SetWithdrawAddress.py new file mode 100644 index 00000000..b317a998 --- /dev/null +++ b/examples/chain_client/distribution/10_SetWithdrawAddress.py @@ -0,0 +1,46 @@ +import asyncio +import os + +import dotenv + +from pyinjective import AsyncClient, PrivateKey +from pyinjective.core.broadcaster import MsgBroadcasterWithPk +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() + client = AsyncClient(network) + composer = await client.composer() + + message_broadcaster = MsgBroadcasterWithPk.new_without_simulation( + network=network, + private_key=configured_private_key, + client=client, + composer=composer, + ) + + 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()) + + withdraw_address = "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r" + + message = composer.msg_set_withdraw_address( + delegator_address=address.to_acc_bech32(), + withdraw_address=withdraw_address, + ) + + # broadcast the transaction + result = await message_broadcaster.broadcast([message]) + print("---Transaction Response---") + print(result) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/11_WithdrawDelegatorReward.py b/examples/chain_client/distribution/11_WithdrawDelegatorReward.py new file mode 100644 index 00000000..02facdc7 --- /dev/null +++ b/examples/chain_client/distribution/11_WithdrawDelegatorReward.py @@ -0,0 +1,47 @@ +import asyncio +import os + +import dotenv + +from pyinjective.async_client import AsyncClient +from pyinjective.core.broadcaster import MsgBroadcasterWithPk +from pyinjective.core.network import Network +from pyinjective.wallet import PrivateKey + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + client = AsyncClient(network) + composer = await client.composer() + + message_broadcaster = MsgBroadcasterWithPk.new_without_simulation( + network=network, + private_key=configured_private_key, + client=client, + composer=composer, + ) + + 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()) + + # prepare tx msg + validator_address = "injvaloper1ultw9r29l8nxy5u6thcgusjn95vsy2caw722q5" + + message = composer.msg_withdraw_delegator_reward( + delegator_address=address.to_acc_bech32(), validator_address=validator_address + ) + + # broadcast the transaction + result = await message_broadcaster.broadcast([message]) + print("---Transaction Response---") + print(result) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/12_WithdrawValidatorCommission.py b/examples/chain_client/distribution/12_WithdrawValidatorCommission.py new file mode 100644 index 00000000..cd351d1f --- /dev/null +++ b/examples/chain_client/distribution/12_WithdrawValidatorCommission.py @@ -0,0 +1,45 @@ +import asyncio +import os + +import dotenv + +from pyinjective.async_client import AsyncClient +from pyinjective.core.broadcaster import MsgBroadcasterWithPk +from pyinjective.core.network import Network +from pyinjective.wallet import PrivateKey + + +async def main() -> None: + dotenv.load_dotenv() + configured_private_key = os.getenv("INJECTIVE_PRIVATE_KEY") + + # select network: local, testnet, mainnet + network = Network.testnet() + client = AsyncClient(network) + composer = await client.composer() + + message_broadcaster = MsgBroadcasterWithPk.new_without_simulation( + network=network, + private_key=configured_private_key, + client=client, + composer=composer, + ) + + 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()) + + # prepare tx msg + validator_address = "injvaloper1ultw9r29l8nxy5u6thcgusjn95vsy2caw722q5" + + message = composer.msg_withdraw_validator_commission(validator_address=validator_address) + + # broadcast the transaction + result = await message_broadcaster.broadcast([message]) + print("---Transaction Response---") + print(result) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/chain_client/distribution/13_FundCommunityPool.py b/examples/chain_client/distribution/13_FundCommunityPool.py new file mode 100644 index 00000000..c60475c8 --- /dev/null +++ b/examples/chain_client/distribution/13_FundCommunityPool.py @@ -0,0 +1,47 @@ +import asyncio +import os +from decimal import Decimal + +import dotenv + +from pyinjective import AsyncClient, PrivateKey +from pyinjective.core.broadcaster import MsgBroadcasterWithPk +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() + client = AsyncClient(network) + composer = await client.composer() + + message_broadcaster = MsgBroadcasterWithPk.new_without_simulation( + network=network, + private_key=configured_private_key, + client=client, + composer=composer, + ) + + 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()) + + amount = composer.create_coin_amount(amount=Decimal("0.1"), token_name="INJ") + + message = composer.msg_fund_community_pool( + amounts=[amount], + depositor=address.to_acc_bech32(), + ) + + # broadcast the transaction + result = await message_broadcaster.broadcast([message]) + print("---Transaction Response---") + print(result) + + +if __name__ == "__main__": + asyncio.get_event_loop().run_until_complete(main()) diff --git a/pyinjective/composer.py b/pyinjective/composer.py index 720920d1..df353d6b 100644 --- a/pyinjective/composer.py +++ b/pyinjective/composer.py @@ -3,6 +3,7 @@ from decimal import Decimal from time import time from typing import Any, Dict, List, Optional +from warnings import warn from google.protobuf import any_pb2, json_format, timestamp_pb2 @@ -13,7 +14,10 @@ from pyinjective.proto.cosmos.authz.v1beta1 import authz_pb2 as cosmos_authz_pb, tx_pb2 as cosmos_authz_tx_pb from pyinjective.proto.cosmos.bank.v1beta1 import bank_pb2 as bank_pb, tx_pb2 as cosmos_bank_tx_pb from pyinjective.proto.cosmos.base.v1beta1 import coin_pb2 as cosmos_dot_base_dot_v1beta1_dot_coin__pb2 -from pyinjective.proto.cosmos.distribution.v1beta1 import tx_pb2 as cosmos_distribution_tx_pb +from pyinjective.proto.cosmos.distribution.v1beta1 import ( + distribution_pb2 as cosmos_distribution_pb2, + tx_pb2 as cosmos_distribution_tx_pb, +) from pyinjective.proto.cosmos.gov.v1beta1 import tx_pb2 as cosmos_gov_tx_pb from pyinjective.proto.cosmos.staking.v1beta1 import tx_pb2 as cosmos_staking_tx_pb from pyinjective.proto.cosmwasm.wasm.v1 import tx_pb2 as wasm_tx_pb @@ -129,8 +133,27 @@ def __init__( self.tokens = tokens def Coin(self, amount: int, denom: str): + """ + This method is deprecated and will be removed soon. Please use `coin` instead + """ + warn("This method is deprecated. Use coin instead", DeprecationWarning, stacklevel=2) return cosmos_dot_base_dot_v1beta1_dot_coin__pb2.Coin(amount=str(amount), denom=denom) + def coin(self, amount: int, denom: str): + """ + This method create an instance of Coin gRPC type, considering the amount is already expressed in chain format + """ + formatted_amount_string = str(int(amount)) + return cosmos_dot_base_dot_v1beta1_dot_coin__pb2.Coin(amount=formatted_amount_string, denom=denom) + + def create_coin_amount(self, amount: Decimal, token_name: str): + """ + This method create an instance of Coin gRPC type, considering the amount is already expressed in chain format + """ + token = self.tokens[token_name] + chain_amount = token.chain_formatted_value(human_readable_value=amount) + return self.coin(amount=int(chain_amount), denom=token.denom) + def get_order_mask(self, **kwargs): order_mask = 0 @@ -331,13 +354,12 @@ def BinaryOptionsOrder( ) def MsgSend(self, from_address: str, to_address: str, amount: float, denom: str): - token = self.tokens[denom] - be_amount = token.chain_formatted_value(human_readable_value=Decimal(str(amount))) + coin = self.create_coin_amount(amount=Decimal(str(amount)), token_name=denom) return cosmos_bank_tx_pb.MsgSend( from_address=from_address, to_address=to_address, - amount=[self.Coin(amount=int(be_amount), denom=token.denom)], + amount=[coin], ) def MsgExecuteContract(self, sender: str, contract: str, msg: str, **kwargs): @@ -350,13 +372,12 @@ def MsgExecuteContract(self, sender: str, contract: str, msg: str, **kwargs): ) def MsgDeposit(self, sender: str, subaccount_id: str, amount: float, denom: str): - token = self.tokens[denom] - be_amount = token.chain_formatted_value(human_readable_value=Decimal(str(amount))) + coin = self.create_coin_amount(amount=Decimal(str(amount)), token_name=denom) return injective_exchange_tx_pb.MsgDeposit( sender=sender, subaccount_id=subaccount_id, - amount=self.Coin(amount=int(be_amount), denom=token.denom), + amount=coin, ) def MsgCreateSpotLimitOrder( @@ -709,24 +730,22 @@ def MsgSubaccountTransfer( amount: int, denom: str, ): - token = self.tokens[denom] - be_amount = token.chain_formatted_value(human_readable_value=Decimal(str(amount))) + be_amount = self.create_coin_amount(amount=Decimal(str(amount)), token_name=denom) return injective_exchange_tx_pb.MsgSubaccountTransfer( sender=sender, source_subaccount_id=source_subaccount_id, destination_subaccount_id=destination_subaccount_id, - amount=self.Coin(amount=int(be_amount), denom=token.denom), + amount=be_amount, ) def MsgWithdraw(self, sender: str, subaccount_id: str, amount: float, denom: str): - token = self.tokens[denom] - be_amount = token.chain_formatted_value(human_readable_value=Decimal(str(amount))) + be_amount = self.create_coin_amount(amount=Decimal(str(amount)), token_name=denom) return injective_exchange_tx_pb.MsgWithdraw( sender=sender, subaccount_id=subaccount_id, - amount=self.Coin(amount=int(be_amount), denom=token.denom), + amount=be_amount, ) def MsgExternalTransfer( @@ -737,14 +756,13 @@ def MsgExternalTransfer( amount: int, denom: str, ): - token = self.tokens[denom] - be_amount = token.chain_formatted_value(human_readable_value=Decimal(str(amount))) + coin = self.create_coin_amount(amount=Decimal(str(amount)), token_name=denom) return injective_exchange_tx_pb.MsgExternalTransfer( sender=sender, source_subaccount_id=source_subaccount_id, destination_subaccount_id=destination_subaccount_id, - amount=self.Coin(amount=int(be_amount), denom=token.denom), + amount=coin, ) def MsgBid(self, sender: str, bid_amount: float, round: float): @@ -753,7 +771,7 @@ def MsgBid(self, sender: str, bid_amount: float, round: float): return injective_auction_tx_pb.MsgBid( sender=sender, round=round, - bid_amount=self.Coin(amount=int(be_amount), denom=INJ_DENOM), + bid_amount=self.coin(amount=int(be_amount), denom=INJ_DENOM), ) def MsgGrantGeneric(self, granter: str, grantee: str, msg_type: str, expire_in: int): @@ -851,15 +869,14 @@ def MsgRelayPriceFeedPrice(self, sender: list, base: list, quote: list, price: l return injective_oracle_tx_pb.MsgRelayPriceFeedPrice(sender=sender, base=base, quote=quote, price=price) def MsgSendToEth(self, denom: str, sender: str, eth_dest: str, amount: float, bridge_fee: float): - token = self.tokens[denom] - be_amount = token.chain_formatted_value(human_readable_value=Decimal(str(amount))) - be_bridge_fee = token.chain_formatted_value(human_readable_value=Decimal(str(bridge_fee))) + be_amount = self.create_coin_amount(amount=Decimal(str(amount)), token_name=denom) + be_bridge_fee = self.create_coin_amount(amount=Decimal(str(bridge_fee)), token_name=denom) return injective_peggy_tx_pb.MsgSendToEth( sender=sender, eth_dest=eth_dest, - amount=self.Coin(amount=int(be_amount), denom=token.denom), - bridge_fee=self.Coin(amount=int(be_bridge_fee), denom=token.denom), + amount=be_amount, + bridge_fee=be_bridge_fee, ) def MsgDelegate(self, delegator_address: str, validator_address: str, amount: float): @@ -868,7 +885,7 @@ def MsgDelegate(self, delegator_address: str, validator_address: str, amount: fl return cosmos_staking_tx_pb.MsgDelegate( delegator_address=delegator_address, validator_address=validator_address, - amount=self.Coin(amount=int(be_amount), denom=INJ_DENOM), + amount=self.coin(amount=int(be_amount), denom=INJ_DENOM), ) def MsgCreateInsuranceFund( @@ -883,7 +900,7 @@ def MsgCreateInsuranceFund( initial_deposit: int, ): token = self.tokens[quote_denom] - be_amount = token.chain_formatted_value(human_readable_value=Decimal(str(initial_deposit))) + deposit = self.create_coin_amount(Decimal(str(initial_deposit)), quote_denom) return injective_insurance_tx_pb.MsgCreateInsuranceFund( sender=sender, @@ -893,7 +910,7 @@ def MsgCreateInsuranceFund( oracle_quote=oracle_quote, oracle_type=oracle_type, expiry=expiry, - initial_deposit=self.Coin(amount=int(be_amount), denom=token.denom), + initial_deposit=deposit, ) def MsgUnderwrite( @@ -903,13 +920,12 @@ def MsgUnderwrite( quote_denom: str, amount: int, ): - token = self.tokens[quote_denom] - be_amount = token.chain_formatted_value(human_readable_value=Decimal(str(amount))) + be_amount = self.create_coin_amount(amount=Decimal(str(amount)), token_name=quote_denom) return injective_insurance_tx_pb.MsgUnderwrite( sender=sender, market_id=market_id, - deposit=self.Coin(amount=int(be_amount), denom=token.denom), + deposit=be_amount, ) def MsgRequestRedemption( @@ -922,17 +938,9 @@ def MsgRequestRedemption( return injective_insurance_tx_pb.MsgRequestRedemption( sender=sender, market_id=market_id, - amount=self.Coin(amount=amount, denom=share_denom), + amount=self.coin(amount=amount, denom=share_denom), ) - def MsgWithdrawDelegatorReward(self, delegator_address: str, validator_address: str): - return cosmos_distribution_tx_pb.MsgWithdrawDelegatorReward( - delegator_address=delegator_address, validator_address=validator_address - ) - - def MsgWithdrawValidatorCommission(self, validator_address: str): - return cosmos_distribution_tx_pb.MsgWithdrawValidatorCommission(validator_address=validator_address) - def MsgVote( self, proposal_id: str, @@ -1101,6 +1109,56 @@ def chain_stream_oracle_price_filter( symbols = symbols or ["*"] return chain_stream_query.OraclePriceFilter(symbol=symbols) + # ------------------------------------------------ + # Distribution module's messages + + def msg_set_withdraw_address(self, delegator_address: str, withdraw_address: str): + return cosmos_distribution_tx_pb.MsgSetWithdrawAddress( + delegator_address=delegator_address, withdraw_address=withdraw_address + ) + + # Deprecated + def MsgWithdrawDelegatorReward(self, delegator_address: str, validator_address: str): + """ + This method is deprecated and will be removed soon. Please use `msg_withdraw_delegator_reward` instead + """ + warn("This method is deprecated. Use msg_withdraw_delegator_reward instead", DeprecationWarning, stacklevel=2) + return cosmos_distribution_tx_pb.MsgWithdrawDelegatorReward( + delegator_address=delegator_address, validator_address=validator_address + ) + + def msg_withdraw_delegator_reward(self, delegator_address: str, validator_address: str): + return cosmos_distribution_tx_pb.MsgWithdrawDelegatorReward( + delegator_address=delegator_address, validator_address=validator_address + ) + + def MsgWithdrawValidatorCommission(self, validator_address: str): + """ + This method is deprecated and will be removed soon. Please use `msg_withdraw_validator_commission` instead + """ + warn( + "This method is deprecated. Use msg_withdraw_validator_commission instead", DeprecationWarning, stacklevel=2 + ) + return cosmos_distribution_tx_pb.MsgWithdrawValidatorCommission(validator_address=validator_address) + + def msg_withdraw_validator_commission(self, validator_address: str): + return cosmos_distribution_tx_pb.MsgWithdrawValidatorCommission(validator_address=validator_address) + + def msg_fund_community_pool(self, amounts: List[cosmos_dot_base_dot_v1beta1_dot_coin__pb2.Coin], depositor: str): + return cosmos_distribution_tx_pb.MsgFundCommunityPool(amount=amounts, depositor=depositor) + + def msg_update_distribution_params(self, authority: str, community_tax: str, withdraw_address_enabled: bool): + params = cosmos_distribution_pb2.Params( + community_tax=community_tax, + withdraw_addr_enabled=withdraw_address_enabled, + ) + return cosmos_distribution_tx_pb.MsgUpdateParams(authority=authority, params=params) + + def msg_community_pool_spend( + self, authority: str, recipient: str, amount: List[cosmos_dot_base_dot_v1beta1_dot_coin__pb2.Coin] + ): + return cosmos_distribution_tx_pb.MsgCommunityPoolSpend(authority=authority, recipient=recipient, amount=amount) + # data field format: [request-msg-header][raw-byte-msg-response] # you need to figure out this magic prefix number to trim request-msg-header off the data # this method handles only exchange responses diff --git a/pyinjective/core/broadcaster.py b/pyinjective/core/broadcaster.py index 22df562b..f279baea 100644 --- a/pyinjective/core/broadcaster.py +++ b/pyinjective/core/broadcaster.py @@ -264,7 +264,7 @@ async def configure_gas_fee_for_transaction( gas_limit = math.ceil(Decimal(str(sim_res["gasInfo"]["gasUsed"])) * self._gas_limit_adjustment_multiplier) fee = [ - self._composer.Coin( + self._composer.coin( amount=self._gas_price * gas_limit, denom=self._client.network.fee_denom, ) @@ -292,7 +292,7 @@ async def configure_gas_fee_for_transaction( transaction_gas_limit = messages_gas_limit + self.TRANSACTION_GAS_LIMIT fee = [ - self._composer.Coin( + self._composer.coin( amount=math.ceil(self._gas_price * transaction_gas_limit), denom=self._client.network.fee_denom, ) diff --git a/tests/test_composer.py b/tests/test_composer.py index 4f756c79..a0cca2ab 100644 --- a/tests/test_composer.py +++ b/tests/test_composer.py @@ -282,7 +282,7 @@ def test_msg_create_denom(self, basic_composer: Composer): def test_msg_mint(self, basic_composer: Composer): sender = "inj1apmvarl2xyv6kecx2ukkeymddw3we4zkygjyc0" - amount = basic_composer.Coin( + amount = basic_composer.coin( amount=1_000_000, denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test", ) @@ -297,7 +297,7 @@ def test_msg_mint(self, basic_composer: Composer): def test_msg_burn(self, basic_composer: Composer): sender = "inj1apmvarl2xyv6kecx2ukkeymddw3we4zkygjyc0" - amount = basic_composer.Coin( + amount = basic_composer.coin( amount=100, denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test", )