From e733927023ce94023baa896054372f3c8e5dba72 Mon Sep 17 00:00:00 2001 From: abel Date: Wed, 3 Jan 2024 16:50:41 -0300 Subject: [PATCH] (feat) Added logic in Composer to create the Tokenfactory related messages to admin tokens. Added new example scripts for all the messages. --- examples/chain_client/71_CreateDenom.py | 35 ++++++ examples/chain_client/72_MsgMint.py | 38 ++++++ examples/chain_client/73_MsgBurn.py | 38 ++++++ .../chain_client/74_MsgSetDenomMetadata.py | 53 ++++++++ examples/chain_client/75_MsgUpdateParams.py | 37 ++++++ examples/chain_client/76_MsgChangeAdmin.py | 37 ++++++ pyinjective/composer.py | 95 +++++++++++++- tests/test_composer.py | 116 ++++++++++++++++++ 8 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 examples/chain_client/71_CreateDenom.py create mode 100644 examples/chain_client/72_MsgMint.py create mode 100644 examples/chain_client/73_MsgBurn.py create mode 100644 examples/chain_client/74_MsgSetDenomMetadata.py create mode 100644 examples/chain_client/75_MsgUpdateParams.py create mode 100644 examples/chain_client/76_MsgChangeAdmin.py diff --git a/examples/chain_client/71_CreateDenom.py b/examples/chain_client/71_CreateDenom.py new file mode 100644 index 00000000..61e76dbf --- /dev/null +++ b/examples/chain_client/71_CreateDenom.py @@ -0,0 +1,35 @@ +import asyncio + +from pyinjective.composer import Composer as ProtoMsgComposer +from pyinjective.core.broadcaster import MsgBroadcasterWithPk +from pyinjective.core.network import Network +from pyinjective.wallet import PrivateKey + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + composer = ProtoMsgComposer(network=network.string()) + private_key_in_hexa = "f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3" + + message_broadcaster = MsgBroadcasterWithPk.new_using_simulation( + network=network, + private_key=private_key_in_hexa, + ) + + priv_key = PrivateKey.from_hex(private_key_in_hexa) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + + message = composer.msg_create_denom( + sender=address.to_acc_bech32(), subdenom="inj_test", name="Injective Test Token", symbol="INJTEST" + ) + + # 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/72_MsgMint.py b/examples/chain_client/72_MsgMint.py new file mode 100644 index 00000000..e96bad93 --- /dev/null +++ b/examples/chain_client/72_MsgMint.py @@ -0,0 +1,38 @@ +import asyncio + +from pyinjective.composer import Composer as ProtoMsgComposer +from pyinjective.core.broadcaster import MsgBroadcasterWithPk +from pyinjective.core.network import Network +from pyinjective.wallet import PrivateKey + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + composer = ProtoMsgComposer(network=network.string()) + private_key_in_hexa = "f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3" + + message_broadcaster = MsgBroadcasterWithPk.new_using_simulation( + network=network, + private_key=private_key_in_hexa, + ) + + priv_key = PrivateKey.from_hex(private_key_in_hexa) + 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") + + message = composer.msg_mint( + sender=address.to_acc_bech32(), + amount=amount, + ) + + # 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/73_MsgBurn.py b/examples/chain_client/73_MsgBurn.py new file mode 100644 index 00000000..cff5a1b4 --- /dev/null +++ b/examples/chain_client/73_MsgBurn.py @@ -0,0 +1,38 @@ +import asyncio + +from pyinjective.composer import Composer as ProtoMsgComposer +from pyinjective.core.broadcaster import MsgBroadcasterWithPk +from pyinjective.core.network import Network +from pyinjective.wallet import PrivateKey + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + composer = ProtoMsgComposer(network=network.string()) + private_key_in_hexa = "f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3" + + message_broadcaster = MsgBroadcasterWithPk.new_using_simulation( + network=network, + private_key=private_key_in_hexa, + ) + + priv_key = PrivateKey.from_hex(private_key_in_hexa) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + + amount = composer.Coin(amount=100, denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test") + + message = composer.msg_burn( + sender=address.to_acc_bech32(), + amount=amount, + ) + + # 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/74_MsgSetDenomMetadata.py b/examples/chain_client/74_MsgSetDenomMetadata.py new file mode 100644 index 00000000..58fa30c0 --- /dev/null +++ b/examples/chain_client/74_MsgSetDenomMetadata.py @@ -0,0 +1,53 @@ +import asyncio + +from pyinjective.composer import Composer as ProtoMsgComposer +from pyinjective.core.broadcaster import MsgBroadcasterWithPk +from pyinjective.core.network import Network +from pyinjective.wallet import PrivateKey + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + composer = ProtoMsgComposer(network=network.string()) + private_key_in_hexa = "f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3" + + message_broadcaster = MsgBroadcasterWithPk.new_without_simulation( + network=network, + private_key=private_key_in_hexa, + ) + + priv_key = PrivateKey.from_hex(private_key_in_hexa) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + + sender = address.to_acc_bech32() + description = "Injective Test Token" + subdenom = "inj_test" + denom = "factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test" + token_decimals = 6 + name = "Injective Test" + symbol = "INJTEST" + uri = "http://injective-test.com/icon.jpg" + uri_hash = "" + + message = composer.msg_set_denom_metadata( + sender=sender, + description=description, + denom=denom, + subdenom=subdenom, + token_decimals=token_decimals, + name=name, + symbol=symbol, + uri=uri, + uri_hash=uri_hash, + ) + + # 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/75_MsgUpdateParams.py b/examples/chain_client/75_MsgUpdateParams.py new file mode 100644 index 00000000..0d53231b --- /dev/null +++ b/examples/chain_client/75_MsgUpdateParams.py @@ -0,0 +1,37 @@ +import asyncio + +from pyinjective.composer import Composer as ProtoMsgComposer +from pyinjective.core.broadcaster import MsgBroadcasterWithPk +from pyinjective.core.network import Network +from pyinjective.wallet import PrivateKey + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + composer = ProtoMsgComposer(network=network.string()) + private_key_in_hexa = "f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3" + + message_broadcaster = MsgBroadcasterWithPk.new_using_simulation( + network=network, + private_key=private_key_in_hexa, + ) + + priv_key = PrivateKey.from_hex(private_key_in_hexa) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + + message = composer.msg_update_params( + authority=address.to_acc_bech32(), + denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test", + amount=1000, + ) + + # 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/76_MsgChangeAdmin.py b/examples/chain_client/76_MsgChangeAdmin.py new file mode 100644 index 00000000..2abc6ec6 --- /dev/null +++ b/examples/chain_client/76_MsgChangeAdmin.py @@ -0,0 +1,37 @@ +import asyncio + +from pyinjective.composer import Composer as ProtoMsgComposer +from pyinjective.core.broadcaster import MsgBroadcasterWithPk +from pyinjective.core.network import Network +from pyinjective.wallet import PrivateKey + + +async def main() -> None: + # select network: local, testnet, mainnet + network = Network.testnet() + composer = ProtoMsgComposer(network=network.string()) + private_key_in_hexa = "f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3" + + message_broadcaster = MsgBroadcasterWithPk.new_without_simulation( + network=network, + private_key=private_key_in_hexa, + ) + + priv_key = PrivateKey.from_hex(private_key_in_hexa) + pub_key = priv_key.to_public_key() + address = pub_key.to_address() + + message = composer.msg_change_admin( + sender=address.to_acc_bech32(), + denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test", + new_admin="inj1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe2hm49", # This is the zero address to remove admin permissions + ) + + # 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 56491b50..e7d2deff 100644 --- a/pyinjective/composer.py +++ b/pyinjective/composer.py @@ -11,7 +11,7 @@ from pyinjective.core.market import BinaryOptionMarket, DerivativeMarket, SpotMarket from pyinjective.core.token import Token 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 tx_pb2 as cosmos_bank_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.gov.v1beta1 import tx_pb2 as cosmos_gov_tx_pb @@ -28,6 +28,10 @@ from pyinjective.proto.injective.oracle.v1beta1 import tx_pb2 as injective_oracle_tx_pb from pyinjective.proto.injective.peggy.v1 import msgs_pb2 as injective_peggy_tx_pb from pyinjective.proto.injective.stream.v1beta1 import query_pb2 as chain_stream_query +from pyinjective.proto.injective.tokenfactory.v1beta1 import ( + params_pb2 as token_factory_params_pb, + tx_pb2 as token_factory_tx_pb, +) REQUEST_TO_RESPONSE_TYPE_MAP = { "MsgCreateSpotLimitOrder": injective_exchange_tx_pb.MsgCreateSpotLimitOrderResponse, @@ -957,6 +961,95 @@ def MsgInstantiateContract( # The coins in the list must be sorted in alphabetical order by denoms. ) + def msg_create_denom( + self, + sender: str, + subdenom: str, + name: str, + symbol: str, + ) -> token_factory_tx_pb.MsgCreateDenom: + return token_factory_tx_pb.MsgCreateDenom( + sender=sender, + subdenom=subdenom, + name=name, + symbol=symbol, + ) + + def msg_mint( + self, + sender: str, + amount: cosmos_dot_base_dot_v1beta1_dot_coin__pb2.Coin, + ) -> token_factory_tx_pb.MsgMint: + return token_factory_tx_pb.MsgMint(sender=sender, amount=amount) + + def msg_burn( + self, + sender: str, + amount: cosmos_dot_base_dot_v1beta1_dot_coin__pb2.Coin, + ) -> token_factory_tx_pb.MsgBurn: + return token_factory_tx_pb.MsgBurn(sender=sender, amount=amount) + + def msg_set_denom_metadata( + self, + sender: str, + description: str, + denom: str, + subdenom: str, + token_decimals: int, + name: str, + symbol: str, + uri: str, + uri_hash: str, + ) -> token_factory_tx_pb.MsgSetDenomMetadata: + micro_denom_unit = bank_pb.DenomUnit( + denom=denom, + exponent=0, + aliases=[f"micro{subdenom}"], + ) + denom_unit = bank_pb.DenomUnit( + denom=subdenom, + exponent=token_decimals, + aliases=[subdenom], + ) + metadata = bank_pb.Metadata( + description=description, + denom_units=[micro_denom_unit, denom_unit], + base=denom, + display=subdenom, + name=name, + symbol=symbol, + uri=uri, + uri_hash=uri_hash, + ) + return token_factory_tx_pb.MsgSetDenomMetadata(sender=sender, metadata=metadata) + + def msg_update_params( + self, + authority: str, + denom: str, + amount: int, + ) -> token_factory_tx_pb.MsgUpdateParams: + coin = self.Coin(amount=amount, denom=denom) + params = token_factory_params_pb.Params( + denom_creation_fee=[coin], + ) + return token_factory_tx_pb.MsgUpdateParams( + authority=authority, + params=params, + ) + + def msg_change_admin( + self, + sender: str, + denom: str, + new_admin: str, + ) -> token_factory_tx_pb.MsgChangeAdmin: + return token_factory_tx_pb.MsgChangeAdmin( + sender=sender, + denom=denom, + new_admin=new_admin, + ) + def chain_stream_bank_balances_filter( self, accounts: Optional[List[str]] = None ) -> chain_stream_query.BankBalancesFilter: diff --git a/tests/test_composer.py b/tests/test_composer.py index f3a9bdc2..392d0e7a 100644 --- a/tests/test_composer.py +++ b/tests/test_composer.py @@ -260,3 +260,119 @@ def test_buy_binary_option_order_creation_without_fixed_denom( assert order.order_type == exchange_pb2.OrderType.BUY assert order.margin == str(int(expected_margin)) assert order.trigger_price == "0" + + def test_msg_create_denom(self, basic_composer: Composer): + sender = "inj1apmvarl2xyv6kecx2ukkeymddw3we4zkygjyc0" + subdenom = "inj-test" + name = "Injective Test" + symbol = "INJTEST" + + message = basic_composer.msg_create_denom( + sender=sender, + subdenom=subdenom, + name=name, + symbol=symbol, + ) + + assert message.sender == sender + assert message.subdenom == subdenom + assert message.name == name + assert message.symbol == symbol + + def test_msg_mint(self, basic_composer: Composer): + sender = "inj1apmvarl2xyv6kecx2ukkeymddw3we4zkygjyc0" + amount = basic_composer.Coin( + amount=1_000_000, + denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test", + ) + + message = basic_composer.msg_mint( + sender=sender, + amount=amount, + ) + + assert message.sender == sender + assert message.amount == amount + + def test_msg_burn(self, basic_composer: Composer): + sender = "inj1apmvarl2xyv6kecx2ukkeymddw3we4zkygjyc0" + amount = basic_composer.Coin( + amount=100, + denom="factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test", + ) + + message = basic_composer.msg_burn( + sender=sender, + amount=amount, + ) + + assert message.sender == sender + assert message.amount == amount + + def test_msg_set_denom_metadata(self, basic_composer: Composer): + sender = "inj1apmvarl2xyv6kecx2ukkeymddw3we4zkygjyc0" + description = "Injective Test Token" + subdenom = "inj_test" + denom = "factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test" + token_decimals = 6 + name = "Injective Test" + symbol = "INJTEST" + uri = "http://injective-test.com/icon.jpg" + uri_hash = "" + + message = basic_composer.msg_set_denom_metadata( + sender=sender, + description=description, + denom=denom, + subdenom=subdenom, + token_decimals=token_decimals, + name=name, + symbol=symbol, + uri=uri, + uri_hash=uri_hash, + ) + + assert message.sender == sender + assert message.metadata.description == description + assert message.metadata.denom_units[0].denom == denom + assert message.metadata.denom_units[0].exponent == 0 + assert message.metadata.denom_units[0].aliases == [f"micro{subdenom}"] + assert message.metadata.denom_units[1].denom == subdenom + assert message.metadata.denom_units[1].exponent == token_decimals + assert message.metadata.denom_units[1].aliases == [subdenom] + assert message.metadata.base == denom + assert message.metadata.display == subdenom + assert message.metadata.name == name + assert message.metadata.symbol == symbol + assert message.metadata.uri == uri + assert message.metadata.uri_hash == uri_hash + + def test_msg_update_params(self, basic_composer: Composer): + authority = "inj1apmvarl2xyv6kecx2ukkeymddw3we4zkygjyc0" + amount = 1000 + denom = "factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test" + + message = basic_composer.msg_update_params( + authority=authority, + denom=denom, + amount=amount, + ) + + assert message.authority == authority + assert message.params.denom_creation_fee[0].amount == str(amount) + assert message.params.denom_creation_fee[0].denom == denom + + def test_msg_change_admin(self, basic_composer): + sender = "inj1apmvarl2xyv6kecx2ukkeymddw3we4zkygjyc0" + denom = "factory/inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r/inj_test" + new_admin = "inj1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe2hm49" + + message = basic_composer.msg_change_admin( + sender=sender, + denom=denom, + new_admin=new_admin, + ) + + assert message.sender == sender + assert message.denom == denom + assert message.new_admin == new_admin