From f07391264bb0f5bbacb4394d58dc5d6616c634ad Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 8 Nov 2024 16:01:28 +0200 Subject: [PATCH 01/17] update cookbook and regenerate docs --- docs/multiversx_sdk.abi.rst | 16 + docs/multiversx_sdk.accounts.rst | 21 + docs/multiversx_sdk.controllers.rst | 69 +++ docs/multiversx_sdk.core.rst | 64 +-- ..._sdk.core.transactions_outcome_parsers.rst | 8 - docs/multiversx_sdk.entrypoints.rst | 37 ++ docs/multiversx_sdk.network_providers.rst | 124 +---- docs/multiversx_sdk.rst | 5 +- examples/Cookbook.ipynb | 440 +++++++----------- 9 files changed, 345 insertions(+), 439 deletions(-) create mode 100644 docs/multiversx_sdk.accounts.rst create mode 100644 docs/multiversx_sdk.controllers.rst create mode 100644 docs/multiversx_sdk.entrypoints.rst diff --git a/docs/multiversx_sdk.abi.rst b/docs/multiversx_sdk.abi.rst index a1a0cf32..0b61e614 100644 --- a/docs/multiversx_sdk.abi.rst +++ b/docs/multiversx_sdk.abi.rst @@ -68,6 +68,14 @@ multiversx\_sdk.abi.bytes\_value module :undoc-members: :show-inheritance: +multiversx\_sdk.abi.code\_metadata\_value module +------------------------------------------------ + +.. automodule:: multiversx_sdk.abi.code_metadata_value + :members: + :undoc-members: + :show-inheritance: + multiversx\_sdk.abi.codec module -------------------------------- @@ -92,6 +100,14 @@ multiversx\_sdk.abi.enum\_value module :undoc-members: :show-inheritance: +multiversx\_sdk.abi.explicit\_enum\_value module +------------------------------------------------ + +.. automodule:: multiversx_sdk.abi.explicit_enum_value + :members: + :undoc-members: + :show-inheritance: + multiversx\_sdk.abi.fields module --------------------------------- diff --git a/docs/multiversx_sdk.accounts.rst b/docs/multiversx_sdk.accounts.rst new file mode 100644 index 00000000..2717c569 --- /dev/null +++ b/docs/multiversx_sdk.accounts.rst @@ -0,0 +1,21 @@ +multiversx\_sdk.accounts package +================================ + +Submodules +---------- + +multiversx\_sdk.accounts.account module +--------------------------------------- + +.. automodule:: multiversx_sdk.accounts.account + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: multiversx_sdk.accounts + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/multiversx_sdk.controllers.rst b/docs/multiversx_sdk.controllers.rst new file mode 100644 index 00000000..56841199 --- /dev/null +++ b/docs/multiversx_sdk.controllers.rst @@ -0,0 +1,69 @@ +multiversx\_sdk.controllers package +=================================== + +Submodules +---------- + +multiversx\_sdk.controllers.account\_controller module +------------------------------------------------------ + +.. automodule:: multiversx_sdk.controllers.account_controller + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.controllers.delegation\_controller module +--------------------------------------------------------- + +.. automodule:: multiversx_sdk.controllers.delegation_controller + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.controllers.interfaces module +--------------------------------------------- + +.. automodule:: multiversx_sdk.controllers.interfaces + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.controllers.relayed\_controller module +------------------------------------------------------ + +.. automodule:: multiversx_sdk.controllers.relayed_controller + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.controllers.smart\_contract\_controller module +-------------------------------------------------------------- + +.. automodule:: multiversx_sdk.controllers.smart_contract_controller + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.controllers.token\_management\_controller module +---------------------------------------------------------------- + +.. automodule:: multiversx_sdk.controllers.token_management_controller + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.controllers.transfers\_controller module +-------------------------------------------------------- + +.. automodule:: multiversx_sdk.controllers.transfers_controller + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: multiversx_sdk.controllers + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/multiversx_sdk.core.rst b/docs/multiversx_sdk.core.rst index e225cf6a..3a38208f 100644 --- a/docs/multiversx_sdk.core.rst +++ b/docs/multiversx_sdk.core.rst @@ -8,22 +8,12 @@ Subpackages :maxdepth: 4 multiversx_sdk.core.proto - multiversx_sdk.core.transaction_builders - multiversx_sdk.core.transaction_parsers multiversx_sdk.core.transactions_factories multiversx_sdk.core.transactions_outcome_parsers Submodules ---------- -multiversx\_sdk.core.account module ------------------------------------ - -.. automodule:: multiversx_sdk.core.account - :members: - :undoc-members: - :show-inheritance: - multiversx\_sdk.core.address module ----------------------------------- @@ -48,30 +38,6 @@ multiversx\_sdk.core.code\_metadata module :undoc-members: :show-inheritance: -multiversx\_sdk.core.codec module ---------------------------------- - -.. automodule:: multiversx_sdk.core.codec - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.core.contract\_query module -------------------------------------------- - -.. automodule:: multiversx_sdk.core.contract_query - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.core.contract\_query\_builder module ----------------------------------------------------- - -.. automodule:: multiversx_sdk.core.contract_query_builder - :members: - :undoc-members: - :show-inheritance: - multiversx\_sdk.core.errors module ---------------------------------- @@ -96,14 +62,6 @@ multiversx\_sdk.core.message module :undoc-members: :show-inheritance: -multiversx\_sdk.core.serializer module --------------------------------------- - -.. automodule:: multiversx_sdk.core.serializer - :members: - :undoc-members: - :show-inheritance: - multiversx\_sdk.core.smart\_contract\_queries\_controller module ---------------------------------------------------------------- @@ -120,14 +78,6 @@ multiversx\_sdk.core.smart\_contract\_query module :undoc-members: :show-inheritance: -multiversx\_sdk.core.token\_payment module ------------------------------------------- - -.. automodule:: multiversx_sdk.core.token_payment - :members: - :undoc-members: - :show-inheritance: - multiversx\_sdk.core.tokens module ---------------------------------- @@ -152,10 +102,18 @@ multiversx\_sdk.core.transaction\_computer module :undoc-members: :show-inheritance: -multiversx\_sdk.core.transaction\_payload module ------------------------------------------------- +multiversx\_sdk.core.transaction\_on\_network module +---------------------------------------------------- + +.. automodule:: multiversx_sdk.core.transaction_on_network + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.core.transaction\_status module +----------------------------------------------- -.. automodule:: multiversx_sdk.core.transaction_payload +.. automodule:: multiversx_sdk.core.transaction_status :members: :undoc-members: :show-inheritance: diff --git a/docs/multiversx_sdk.core.transactions_outcome_parsers.rst b/docs/multiversx_sdk.core.transactions_outcome_parsers.rst index a7a4b328..a26babd2 100644 --- a/docs/multiversx_sdk.core.transactions_outcome_parsers.rst +++ b/docs/multiversx_sdk.core.transactions_outcome_parsers.rst @@ -20,14 +20,6 @@ multiversx\_sdk.core.transactions\_outcome\_parsers.delegation\_transactions\_ou :undoc-members: :show-inheritance: -multiversx\_sdk.core.transactions\_outcome\_parsers.resources module --------------------------------------------------------------------- - -.. automodule:: multiversx_sdk.core.transactions_outcome_parsers.resources - :members: - :undoc-members: - :show-inheritance: - multiversx\_sdk.core.transactions\_outcome\_parsers.smart\_contract\_transactions\_outcome\_parser module --------------------------------------------------------------------------------------------------------- diff --git a/docs/multiversx_sdk.entrypoints.rst b/docs/multiversx_sdk.entrypoints.rst new file mode 100644 index 00000000..fd78a70c --- /dev/null +++ b/docs/multiversx_sdk.entrypoints.rst @@ -0,0 +1,37 @@ +multiversx\_sdk.entrypoints package +=================================== + +Submodules +---------- + +multiversx\_sdk.entrypoints.config module +----------------------------------------- + +.. automodule:: multiversx_sdk.entrypoints.config + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.entrypoints.entrypoints module +---------------------------------------------- + +.. automodule:: multiversx_sdk.entrypoints.entrypoints + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.entrypoints.errors module +----------------------------------------- + +.. automodule:: multiversx_sdk.entrypoints.errors + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: multiversx_sdk.entrypoints + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/multiversx_sdk.network_providers.rst b/docs/multiversx_sdk.network_providers.rst index 60b116d2..74aa7df8 100644 --- a/docs/multiversx_sdk.network_providers.rst +++ b/docs/multiversx_sdk.network_providers.rst @@ -4,14 +4,6 @@ multiversx\_sdk.network\_providers package Submodules ---------- -multiversx\_sdk.network\_providers.accounts module --------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.accounts - :members: - :undoc-members: - :show-inheritance: - multiversx\_sdk.network\_providers.api\_network\_provider module ---------------------------------------------------------------- @@ -28,30 +20,6 @@ multiversx\_sdk.network\_providers.config module :undoc-members: :show-inheritance: -multiversx\_sdk.network\_providers.contract\_query\_requests module -------------------------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.contract_query_requests - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.contract\_query\_response module -------------------------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.contract_query_response - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.contract\_results module ------------------------------------------------------------ - -.. automodule:: multiversx_sdk.network_providers.contract_results - :members: - :undoc-members: - :show-inheritance: - multiversx\_sdk.network\_providers.errors module ------------------------------------------------ @@ -60,42 +28,18 @@ multiversx\_sdk.network\_providers.errors module :undoc-members: :show-inheritance: -multiversx\_sdk.network\_providers.interface module ---------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.interface - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.network\_config module +multiversx\_sdk.network\_providers.http\_resources module --------------------------------------------------------- -.. automodule:: multiversx_sdk.network_providers.network_config +.. automodule:: multiversx_sdk.network_providers.http_resources :members: :undoc-members: :show-inheritance: -multiversx\_sdk.network\_providers.network\_general\_statistics module ----------------------------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.network_general_statistics - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.network\_stake module --------------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.network_stake - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.network\_status module ---------------------------------------------------------- +multiversx\_sdk.network\_providers.interface module +--------------------------------------------------- -.. automodule:: multiversx_sdk.network_providers.network_status +.. automodule:: multiversx_sdk.network_providers.interface :members: :undoc-members: :show-inheritance: @@ -116,18 +60,10 @@ multiversx\_sdk.network\_providers.resources module :undoc-members: :show-inheritance: -multiversx\_sdk.network\_providers.token\_definitions module ------------------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.token_definitions - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.tokens module +multiversx\_sdk.network\_providers.shared module ------------------------------------------------ -.. automodule:: multiversx_sdk.network_providers.tokens +.. automodule:: multiversx_sdk.network_providers.shared :members: :undoc-members: :show-inheritance: @@ -148,50 +84,10 @@ multiversx\_sdk.network\_providers.transaction\_decoder module :undoc-members: :show-inheritance: -multiversx\_sdk.network\_providers.transaction\_events module -------------------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.transaction_events - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.transaction\_logs module ------------------------------------------------------------ - -.. automodule:: multiversx_sdk.network_providers.transaction_logs - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.transaction\_receipt module --------------------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.transaction_receipt - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.transaction\_status module -------------------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.transaction_status - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.transactions module ------------------------------------------------------- - -.. automodule:: multiversx_sdk.network_providers.transactions - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.network\_providers.utils module ------------------------------------------------ +multiversx\_sdk.network\_providers.user\_agent module +----------------------------------------------------- -.. automodule:: multiversx_sdk.network_providers.utils +.. automodule:: multiversx_sdk.network_providers.user_agent :members: :undoc-members: :show-inheritance: diff --git a/docs/multiversx_sdk.rst b/docs/multiversx_sdk.rst index 45d0f95f..4db166f2 100644 --- a/docs/multiversx_sdk.rst +++ b/docs/multiversx_sdk.rst @@ -8,9 +8,10 @@ Subpackages :maxdepth: 4 multiversx_sdk.abi - multiversx_sdk.adapters - multiversx_sdk.converters + multiversx_sdk.accounts + multiversx_sdk.controllers multiversx_sdk.core + multiversx_sdk.entrypoints multiversx_sdk.network_providers multiversx_sdk.wallet diff --git a/examples/Cookbook.ipynb b/examples/Cookbook.ipynb index e2002035..e2f43b6a 100644 --- a/examples/Cookbook.ipynb +++ b/examples/Cookbook.ipynb @@ -63,6 +63,8 @@ "metadata": {}, "outputs": [], "source": [ + "from multiversx_sdk import Address\n", + "\n", "address = Address.new_from_hex(\"0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1\", \"erd\")" ] }, @@ -79,6 +81,8 @@ "metadata": {}, "outputs": [], "source": [ + "from multiversx_sdk import Address\n", + "\n", "pubkey = bytes.fromhex(\"0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1\")\n", "address = Address(pubkey, \"erd\")" ] @@ -152,9 +156,11 @@ } ], "source": [ - "from multiversx_sdk import AddressComputer\n", + "from multiversx_sdk import Address, AddressComputer\n", + "\n", + "address_computer = AddressComputer()\n", + "address = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", "\n", - "address_computer = AddressComputer(number_of_shards=3)\n", "print(\"Shard:\", address_computer.get_shard_of_address(address))" ] }, @@ -174,14 +180,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "Is contract: True\n" + "Is contract address: True\n" ] } ], "source": [ - "address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm\")\n", + "from multiversx_sdk import Address\n", "\n", - "print(\"Is contract:\", address.is_smart_contract())" + "address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm\")\n", + "print(\"Is contract address:\", address.is_smart_contract())" ] }, { @@ -207,25 +214,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'nonce': 77, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 50000, 'data': '', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n" - "{'nonce': 77, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 50000, 'data': '', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n" + "{'nonce': 77, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 50000, 'data': '', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n" ] } ], "source": [ - "from multiversx_sdk import Transaction, TransactionsConverter\n", + "from multiversx_sdk import Address, Transaction\n", "\n", "transaction = Transaction(\n", - " sender=\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\",\n", - " receiver=\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\",\n", + " sender=Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\"),\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " gas_limit=50000,\n", " chain_id=\"D\",\n", " nonce=77,\n", " value=1000000000000000000\n", ")\n", "\n", - "transaction_converter = TransactionsConverter()\n", - "print(transaction_converter.transaction_to_dictionary(transaction))" + "print(transaction.to_dictionary())" ] }, { @@ -258,15 +263,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'nonce': 77, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 50000, 'data': 'Zm9yIHRoZSBib29r', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n" - "{'nonce': 77, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 50000, 'data': 'Zm9yIHRoZSBib29r', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n" + "{'nonce': 77, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 50000, 'data': 'Zm9yIHRoZSBib29r', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n" ] } ], "source": [ + "from multiversx_sdk import Address, Transaction\n", + "\n", "transaction = Transaction(\n", - " sender=\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\",\n", - " receiver=\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\",\n", + " sender=Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\"),\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " gas_limit=50000,\n", " chain_id=\"D\",\n", " nonce=77,\n", @@ -274,7 +280,7 @@ " data=b\"for the book\"\n", ")\n", "\n", - "print(transaction_converter.transaction_to_dictionary(transaction))" + "print(transaction.to_dictionary())" ] }, { @@ -311,14 +317,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 68000, 'data': 'Zm9yIHRoZSBib29r', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", - "Transaction: {'nonce': 0, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 68000, 'data': 'Zm9yIHRoZSBib29r', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 68000, 'data': 'Zm9yIHRoZSBib29r', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: for the book\n" ] } ], "source": [ - "from multiversx_sdk import TransferTransactionsFactory\n", + "from multiversx_sdk import Address, TransferTransactionsFactory\n", "\n", "transfer_factory = TransferTransactionsFactory(config=config)\n", "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", @@ -332,7 +337,7 @@ " data=\"for the book\"\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(transaction))\n", + "print(\"Transaction:\", transaction.to_dictionary())\n", "print(\"Transaction data:\", transaction.data.decode())" ] }, @@ -352,8 +357,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 410000, 'data': 'RVNEVFRyYW5zZmVyQDU0NDU1MzU0MmQzODYyMzAzMjM4NjZAMjcxMA==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 410000, 'data': 'RVNEVFRyYW5zZmVyQDU0NDU1MzU0MmQzODYyMzAzMjM4NjZAMjcxMA==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 410000, 'data': 'RVNEVFRyYW5zZmVyQDU0NDU1MzU0MmQzODYyMzAzMjM4NjZAMjcxMA==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: ESDTTransfer@544553542d386230323866@2710\n" ] } @@ -370,7 +374,7 @@ " token_transfers=[transfer]\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(transaction))\n", + "print(\"Transaction:\", transaction.to_dictionary())\n", "print(\"Transaction data:\", transaction.data.decode())" ] }, @@ -392,8 +396,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1213500, 'data': 'RVNEVE5GVFRyYW5zZmVyQDU0NDU1MzU0MmQzMzM4NjYzMjM0MzlAMDFAMDFAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOA==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1213500, 'data': 'RVNEVE5GVFRyYW5zZmVyQDU0NDU1MzU0MmQzMzM4NjYzMjM0MzlAMDFAMDFAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOA==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1213500, 'data': 'RVNEVE5GVFRyYW5zZmVyQDU0NDU1MzU0MmQzMzM4NjYzMjM0MzlAMDFAMDFAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOA==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: ESDTNFTTransfer@544553542d333866323439@01@01@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8\n" ] } @@ -408,7 +411,7 @@ " token_transfers=[transfer]\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(transaction))\n", + "print(\"Transaction:\", transaction.to_dictionary())\n", "print(\"Transaction data:\", transaction.data.decode())" ] }, @@ -428,8 +431,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1213500, 'data': 'RVNEVE5GVFRyYW5zZmVyQDUzNDU0ZDQ5MmQzOTY1NjY2NDMwNjZAMDFAMDVAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOA==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1213500, 'data': 'RVNEVE5GVFRyYW5zZmVyQDUzNDU0ZDQ5MmQzOTY1NjY2NDMwNjZAMDFAMDVAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOA==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1213500, 'data': 'RVNEVE5GVFRyYW5zZmVyQDUzNDU0ZDQ5MmQzOTY1NjY2NDMwNjZAMDFAMDVAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOA==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: ESDTNFTTransfer@53454d492d396566643066@01@05@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8\n" ] } @@ -444,7 +446,7 @@ " token_transfers=[transfer]\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(transaction))\n", + "print(\"Transaction:\", transaction.to_dictionary())\n", "print(\"Transaction data:\", transaction.data.decode())" ] }, @@ -464,8 +466,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1484000, 'data': 'TXVsdGlFU0RUTkZUVHJhbnNmZXJAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOEAwMkA1NDQ1NTM1NDJkMzMzODY2MzIzNDM5QDAxQDAxQDQyNDE1MjJkNjMzODMwNjQzMjM5QEA4YWM3MjMwNDg5ZTgwMDAw', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1484000, 'data': 'TXVsdGlFU0RUTkZUVHJhbnNmZXJAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOEAwMkA1NDQ1NTM1NDJkMzMzODY2MzIzNDM5QDAxQDAxQDQyNDE1MjJkNjMzODMwNjQzMjM5QEA4YWM3MjMwNDg5ZTgwMDAw', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1484000, 'data': 'TXVsdGlFU0RUTkZUVHJhbnNmZXJAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOEAwMkA1NDQ1NTM1NDJkMzMzODY2MzIzNDM5QDAxQDAxQDQyNDE1MjJkNjMzODMwNjQzMjM5QEA4YWM3MjMwNDg5ZTgwMDAw', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@02@544553542d333866323439@01@01@4241522d633830643239@@8ac7230489e80000\n" ] } @@ -483,7 +484,7 @@ " token_transfers=[first_transfer, second_transfer]\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(transaction))\n", + "print(\"Transaction:\", transaction.to_dictionary())\n", "print(\"Transaction data:\", transaction.data.decode())" ] }, @@ -505,13 +506,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 68000, 'data': 'Zm9yIHRoZSBib29r', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '1000000000000000000', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 68000, 'data': 'Zm9yIHRoZSBib29r', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: for the book\n" ] } ], "source": [ - "from multiversx_sdk import TransferTransactionsFactory, TransactionsFactoryConfig\n", + "from multiversx_sdk import Address, TransferTransactionsFactory, TransactionsFactoryConfig\n", "\n", "transfer_factory = TransferTransactionsFactory(config=TransactionsFactoryConfig(chain_id=\"D\"))\n", "\n", @@ -526,7 +527,7 @@ " data=\"for the book\".encode()\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(transaction))\n", + "print(\"Transaction:\", transaction.to_dictionary())\n", "print(\"Transaction data:\", transaction.data.decode())" ] }, @@ -546,7 +547,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1484000, 'data': 'TXVsdGlFU0RUTkZUVHJhbnNmZXJAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOEAwMkA1NDQ1NTM1NDJkMzMzODY2MzIzNDM5QDAxQDAxQDQyNDE1MjJkNjMzODMwNjQzMjM5QEA4YWM3MjMwNDg5ZTgwMDAw', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1484000, 'data': 'TXVsdGlFU0RUTkZUVHJhbnNmZXJAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOEAwMkA1NDQ1NTM1NDJkMzMzODY2MzIzNDM5QDAxQDAxQDQyNDE1MjJkNjMzODMwNjQzMjM5QEA4YWM3MjMwNDg5ZTgwMDAw', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@02@544553542d333866323439@01@01@4241522d633830643239@@8ac7230489e80000\n" ] } @@ -564,7 +565,7 @@ " token_transfers=[first_transfer, second_transfer]\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(transaction))\n", + "print(\"Transaction:\", transaction.to_dictionary())\n", "print(\"Transaction data:\", transaction.data.decode())" ] }, @@ -584,7 +585,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1745500, 'data': 'TXVsdGlFU0RUTkZUVHJhbnNmZXJAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOEAwM0A1NDQ1NTM1NDJkMzMzODY2MzIzNDM5QDAxQDAxQDQyNDE1MjJkNjMzODMwNjQzMjM5QEA4YWM3MjMwNDg5ZTgwMDAwQDQ1NDc0YzQ0MmQzMDMwMzAzMDMwMzBAQDBkZTBiNmIzYTc2NDAwMDA=', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 1745500, 'data': 'TXVsdGlFU0RUTkZUVHJhbnNmZXJAODA0OWQ2MzllNWE2OTgwZDFjZDIzOTJhYmNjZTQxMDI5Y2RhNzRhMTU2MzUyM2EyMDJmMDk2NDFjYzI2MThmOEAwM0A1NDQ1NTM1NDJkMzMzODY2MzIzNDM5QDAxQDAxQDQyNDE1MjJkNjMzODMwNjQzMjM5QEA4YWM3MjMwNDg5ZTgwMDAwQDQ1NDc0YzQ0MmQzMDMwMzAzMDMwMzBAQDBkZTBiNmIzYTc2NDAwMDA=', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@03@544553542d333866323439@01@01@4241522d633830643239@@8ac7230489e80000@45474c442d303030303030@@0de0b6b3a7640000\n" ] } @@ -603,7 +604,7 @@ " token_transfers=[first_transfer, second_transfer]\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(transaction))\n", + "print(\"Transaction:\", transaction.to_dictionary())\n", "print(\"Transaction data:\", transaction.data.decode())" ] }, @@ -697,8 +698,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'nonce': 2627, 'value': '0', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 61052000, 'data': 'cmVsYXllZFR4QDdiMjI2ZTZmNmU2MzY1MjIzYTMxMzkzODJjMjI3MzY1NmU2NDY1NzIyMjNhMjI2NzQ1NmU1NzRmNjU1NzZkNmQ0MTMwNjMzMDZhNmI3MTc2NGQzNTQyNDE3MDdhNjE2NDRiNDY1NzRlNTM0ZjY5NDE3NjQzNTc1MTYzNzc2ZDQ3NTA2NzNkMjIyYzIyNzI2NTYzNjU2OTc2NjU3MjIyM2EyMjQxNDE0MTQxNDE0MTQxNDE0MTQxNDE0NjQxNDE1OTQ2NGUyYjRkNGU1NTY5MzUzODY3NjI0MjMzMzUyYjYzNTY2NzczNGM1OTM5NzI2NzY1NjE2NTQ1M2QyMjJjMjI3NjYxNmM3NTY1MjIzYTMwMmMyMjY3NjE3MzUwNzI2OTYzNjUyMjNhMzEzMDMwMzAzMDMwMzAzMDMwMzAyYzIyNjc2MTczNGM2OTZkNjk3NDIyM2EzNjMwMzAzMDMwMzAzMDMwMmMyMjY0NjE3NDYxMjIzYTIyNTk1NzUyNmI1MTQ0NDEzMTIyMmMyMjczNjk2NzZlNjE3NDc1NzI2NTIyM2EyMjQ3NGM0MzM0NGE1OTM1NGMzMDc1NmY0YTRkNzA0ODc1MzQ0ODUwNTc2NzQ3NDE0ODQ3NmY0NzZhN2E0NzYxMzYzNTJiNzU0ODMwMzAyZjc3NGM1MTU5NDU1MTUyMzA2NDVhNGM2MzUxMzQ1NDZkNzE1NTM5NGI2ZDUxNWEzNTU0NzE2ZDRjNmU1MzUxMzQ1NjU3NDY2ZjM1NDM2Njc5Mzk2MzY0MmI2YzQzNDEzZDNkMjIyYzIyNjM2ODYxNjk2ZTQ5NDQyMjNhMjI1MjQxM2QzZDIyMmMyMjc2NjU3MjczNjk2ZjZlMjIzYTMyN2Q=', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n" - "{'nonce': 2627, 'value': '0', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 61052000, 'data': 'cmVsYXllZFR4QDdiMjI2ZTZmNmU2MzY1MjIzYTMxMzkzODJjMjI3MzY1NmU2NDY1NzIyMjNhMjI2NzQ1NmU1NzRmNjU1NzZkNmQ0MTMwNjMzMDZhNmI3MTc2NGQzNTQyNDE3MDdhNjE2NDRiNDY1NzRlNTM0ZjY5NDE3NjQzNTc1MTYzNzc2ZDQ3NTA2NzNkMjIyYzIyNzI2NTYzNjU2OTc2NjU3MjIyM2EyMjQxNDE0MTQxNDE0MTQxNDE0MTQxNDE0NjQxNDE1OTQ2NGUyYjRkNGU1NTY5MzUzODY3NjI0MjMzMzUyYjYzNTY2NzczNGM1OTM5NzI2NzY1NjE2NTQ1M2QyMjJjMjI3NjYxNmM3NTY1MjIzYTMwMmMyMjY3NjE3MzUwNzI2OTYzNjUyMjNhMzEzMDMwMzAzMDMwMzAzMDMwMzAyYzIyNjc2MTczNGM2OTZkNjk3NDIyM2EzNjMwMzAzMDMwMzAzMDMwMmMyMjY0NjE3NDYxMjIzYTIyNTk1NzUyNmI1MTQ0NDEzMTIyMmMyMjczNjk2NzZlNjE3NDc1NzI2NTIyM2EyMjQ3NGM0MzM0NGE1OTM1NGMzMDc1NmY0YTRkNzA0ODc1MzQ0ODUwNTc2NzQ3NDE0ODQ3NmY0NzZhN2E0NzYxMzYzNTJiNzU0ODMwMzAyZjc3NGM1MTU5NDU1MTUyMzA2NDVhNGM2MzUxMzQ1NDZkNzE1NTM5NGI2ZDUxNWEzNTU0NzE2ZDRjNmU1MzUxMzQ1NjU3NDY2ZjM1NDM2Njc5Mzk2MzY0MmI2YzQzNDEzZDNkMjIyYzIyNjM2ODYxNjk2ZTQ5NDQyMjNhMjI1MjQxM2QzZDIyMmMyMjc2NjU3MjczNjk2ZjZlMjIzYTMyN2Q=', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n" + "{'nonce': 2627, 'value': '0', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 61052000, 'data': 'cmVsYXllZFR4QDdiMjI2ZTZmNmU2MzY1MjIzYTMxMzkzODJjMjI3MzY1NmU2NDY1NzIyMjNhMjI2NzQ1NmU1NzRmNjU1NzZkNmQ0MTMwNjMzMDZhNmI3MTc2NGQzNTQyNDE3MDdhNjE2NDRiNDY1NzRlNTM0ZjY5NDE3NjQzNTc1MTYzNzc2ZDQ3NTA2NzNkMjIyYzIyNzI2NTYzNjU2OTc2NjU3MjIyM2EyMjQxNDE0MTQxNDE0MTQxNDE0MTQxNDE0NjQxNDE1OTQ2NGUyYjRkNGU1NTY5MzUzODY3NjI0MjMzMzUyYjYzNTY2NzczNGM1OTM5NzI2NzY1NjE2NTQ1M2QyMjJjMjI3NjYxNmM3NTY1MjIzYTMwMmMyMjY3NjE3MzUwNzI2OTYzNjUyMjNhMzEzMDMwMzAzMDMwMzAzMDMwMzAyYzIyNjc2MTczNGM2OTZkNjk3NDIyM2EzNjMwMzAzMDMwMzAzMDMwMmMyMjY0NjE3NDYxMjIzYTIyNTk1NzUyNmI1MTQ0NDEzMTIyMmMyMjczNjk2NzZlNjE3NDc1NzI2NTIyM2EyMjQ3NGM0MzM0NGE1OTM1NGMzMDc1NmY0YTRkNzA0ODc1MzQ0ODUwNTc2NzQ3NDE0ODQ3NmY0NzZhN2E0NzYxMzYzNTJiNzU0ODMwMzAyZjc3NGM1MTU5NDU1MTUyMzA2NDVhNGM2MzUxMzQ1NDZkNzE1NTM5NGI2ZDUxNWEzNTU0NzE2ZDRjNmU1MzUxMzQ1NjU3NDY2ZjM1NDM2Njc5Mzk2MzY0MmI2YzQzNDEzZDNkMjIyYzIyNjM2ODYxNjk2ZTQ5NDQyMjNhMjI1MjQxM2QzZDIyMmMyMjc2NjU3MjczNjk2ZjZlMjIzYTMyN2Q=', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n" ] } ], @@ -707,8 +707,6 @@ "\n", "from multiversx_sdk import (Address, RelayedTransactionsFactory, Transaction,\n", " TransactionComputer, TransactionsFactoryConfig,\n", - "from multiversx_sdk import (Address, RelayedTransactionsFactory, Transaction,\n", - " TransactionComputer, TransactionsFactoryConfig,\n", " UserSigner)\n", "\n", "signer = UserSigner.from_pem_file(Path(\"../multiversx_sdk/testutils/testwallets/bob.pem\"))\n", @@ -716,8 +714,8 @@ "\n", "inner_tx = Transaction(\n", " chain_id=network_config.chain_id,\n", - " sender=\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\",\n", - " receiver=\"erd1qqqqqqqqqqqqqpgqqczn0ccd2gh8eqdswln7w9vzctv0dwq7d8ssm4y34z\",\n", + " sender=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " receiver=Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgqqczn0ccd2gh8eqdswln7w9vzctv0dwq7d8ssm4y34z\"),\n", " gas_limit=60000000,\n", " nonce=198,\n", " data=b\"add@05\"\n", @@ -734,7 +732,7 @@ ")\n", "relayed_tx.nonce = 2627\n", "\n", - "print(transaction_converter.transaction_to_dictionary(relayed_tx))" + "print(relayed_tx.to_dictionary())" ] }, { @@ -760,8 +758,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'nonce': 37, 'value': '0', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 60381500, 'data': 'cmVsYXllZFR4VjJAMDAwMDAwMDAwMDAwMDAwMDA1MDAwNjA1MzdlMzBkNTIyZTdjODFiMDc3ZTdlNzE1ODJjMmQ4ZjZiODFlNjllMUAwZkA2MTY0NjQ0MDMwMzVANjUwNzJhOTExOGRiMTQ3NDdhNGYxY2Y4NTdhNmU0MzczZTNlOWVhZDZlYTYwMzZmNTExMWJmNjg4MDNjNWNhZGZkYzQ0ZDM3YTIxYjkyMDQ0OWI1ZTgzYzc4Mzk3NTQyNWMzZjQ3NjAyMDE3M2FjMGZjNzE0ODE2NDZkNGE5MDA=', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n" - "{'nonce': 37, 'value': '0', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 60381500, 'data': 'cmVsYXllZFR4VjJAMDAwMDAwMDAwMDAwMDAwMDA1MDAwNjA1MzdlMzBkNTIyZTdjODFiMDc3ZTdlNzE1ODJjMmQ4ZjZiODFlNjllMUAwZkA2MTY0NjQ0MDMwMzVANjUwNzJhOTExOGRiMTQ3NDdhNGYxY2Y4NTdhNmU0MzczZTNlOWVhZDZlYTYwMzZmNTExMWJmNjg4MDNjNWNhZGZkYzQ0ZDM3YTIxYjkyMDQ0OWI1ZTgzYzc4Mzk3NTQyNWMzZjQ3NjAyMDE3M2FjMGZjNzE0ODE2NDZkNGE5MDA=', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n" + "{'nonce': 37, 'value': '0', 'receiver': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 60381500, 'data': 'cmVsYXllZFR4VjJAMDAwMDAwMDAwMDAwMDAwMDA1MDAwNjA1MzdlMzBkNTIyZTdjODFiMDc3ZTdlNzE1ODJjMmQ4ZjZiODFlNjllMUAwZkA2MTY0NjQ0MDMwMzVANjUwNzJhOTExOGRiMTQ3NDdhNGYxY2Y4NTdhNmU0MzczZTNlOWVhZDZlYTYwMzZmNTExMWJmNjg4MDNjNWNhZGZkYzQ0ZDM3YTIxYjkyMDQ0OWI1ZTgzYzc4Mzk3NTQyNWMzZjQ3NjAyMDE3M2FjMGZjNzE0ODE2NDZkNGE5MDA=', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n" ] } ], @@ -770,8 +767,6 @@ "\n", "from multiversx_sdk import (Address, RelayedTransactionsFactory, Transaction,\n", " TransactionComputer, TransactionsFactoryConfig,\n", - "from multiversx_sdk import (Address, RelayedTransactionsFactory, Transaction,\n", - " TransactionComputer, TransactionsFactoryConfig,\n", " UserSigner)\n", "\n", "signer = UserSigner.from_pem_file(Path(\"../multiversx_sdk/testutils/testwallets/bob.pem\"))\n", @@ -780,8 +775,8 @@ "# for the relayedV2 transactions, the gasLimit for the inner transaction should be 0\n", "inner_tx = Transaction(\n", " chain_id=network_config.chain_id,\n", - " sender=\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\",\n", - " receiver=\"erd1qqqqqqqqqqqqqpgqqczn0ccd2gh8eqdswln7w9vzctv0dwq7d8ssm4y34z\",\n", + " sender=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " receiver=Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgqqczn0ccd2gh8eqdswln7w9vzctv0dwq7d8ssm4y34z\"),\n", " gas_limit=0,\n", " nonce=15,\n", " data=b\"add@05\"\n", @@ -799,71 +794,7 @@ ")\n", "relayed_tx.nonce = 37\n", "\n", - "print(transaction_converter.transaction_to_dictionary(relayed_tx))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Relayed V3\n", - "\n", - "This is the latest iteration of relayed transactions and we highly recommend using this version over the previous ones as soon as it becomes available on the network." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 110000, 'data': '', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': [{'nonce': 15, 'value': '0', 'receiver': 'erd1qqqqqqqqqqqqqpgqqczn0ccd2gh8eqdswln7w9vzctv0dwq7d8ssm4y34z', 'sender': 'erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 60000, 'data': 'aGVsbG8=', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '730ae25bda39d5aaa25cc6f8e27c1cc5fa67869464559187dcdd02e22b88e561bf4660718791200a37290d2ff8c73449c1ef6ef38ddce7b36c89931d8fa2f10b', 'guardianSignature': '', 'relayer': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'innerTransactions': []}]}\n" - ] - } - ], - "source": [ - "from pathlib import Path\n", - "\n", - "from multiversx_sdk import (Address, RelayedTransactionsFactory, Transaction,\n", - " TransactionComputer, TransactionsFactoryConfig,\n", - " UserSigner)\n", - "\n", - "bob = UserSigner.from_pem_file(Path(\"../multiversx_sdk/testutils/testwallets/bob.pem\"))\n", - "relayer = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", - "\n", - "inner_tx = Transaction(\n", - " chain_id=network_config.chain_id,\n", - " sender=\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\",\n", - " receiver=\"erd1qqqqqqqqqqqqqpgqqczn0ccd2gh8eqdswln7w9vzctv0dwq7d8ssm4y34z\",\n", - " gas_limit=60000,\n", - " nonce=15,\n", - " data=b\"hello\",\n", - " relayer=relayer.to_bech32()\n", - " )\n", - "transaction_computer = TransactionComputer()\n", - "inner_tx.signature = bob.sign(transaction_computer.compute_bytes_for_signing(inner_tx))\n", - "\n", - "config = TransactionsFactoryConfig(chain_id=\"D\")\n", - "factory = RelayedTransactionsFactory(config=config)\n", - "\n", - "relayed_tx = factory.create_relayed_v3_transaction(\n", - " relayer_address=relayer,\n", - " inner_transactions=[inner_tx]\n", - ")\n", - "\n", - "print(transaction_converter.transaction_to_dictionary(relayed_tx))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A few things worth mentioning before moving further, the `data` field of the relayer's transaction **should not** be set and the relayer should be in the same shard as the sender of the inner transaction.\n", - "\n", - "Don't forget, after the Relayed V3 transaction is created, the `nonce` needs to be set and the transaction should be signed before broadcasting it." + "print(relayed_tx.to_dictionary())" ] }, { @@ -884,7 +815,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -903,7 +834,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -926,7 +857,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -953,7 +884,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -998,7 +929,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -1018,12 +949,13 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ - "from multiversx_sdk import SmartContractTransactionsFactory\n", + "from multiversx_sdk import SmartContractTransactionsFactory, TransactionsFactoryConfig\n", "\n", + "config = TransactionsFactoryConfig(chain_id=\"D\")\n", "factory = SmartContractTransactionsFactory(config)" ] }, @@ -1036,10 +968,11 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ + "config = TransactionsFactoryConfig(chain_id=\"D\")\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "factory = SmartContractTransactionsFactory(config, abi)" ] @@ -1053,15 +986,14 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'MDA2MTczNmQwMTAwMDAwMDAxMjkwODYwMDAwMDYwMDAwMTdmNjAwMjdmN2YwMTdmNjAwMjdmN2YwMDYwMDE3ZjAwNjAwMzdmN2Y3ZjAxN2Y2MDAzN2Y3ZjdmMDA2MDAxN2YwMTdmMDI5MDAyMGIwMzY1NmU3NjE5NjI2OTY3NDk2ZTc0NDc2NTc0NTU2ZTczNjk2NzZlNjU2NDQxNzI2Nzc1NmQ2NTZlNzQwMDAzMDM2NTZlNzYwZjY3NjU3NDRlNzU2ZDQxNzI2Nzc1NmQ2NTZlNzQ3MzAwMDEwMzY1NmU3NjBiNzM2OTY3NmU2MTZjNDU3MjcyNmY3MjAwMDMwMzY1NmU3NjEyNmQ0Mjc1NjY2NjY1NzI1Mzc0NmY3MjYxNjc2NTRjNmY2MTY0MDAwMjAzNjU2ZTc2MTc2ZDQyNzU2NjY2NjU3MjU0NmY0MjY5Njc0OTZlNzQ1NTZlNzM2OTY3NmU2NTY0MDAwMjAzNjU2ZTc2MTk2ZDQyNzU2NjY2NjU3MjQ2NzI2ZjZkNDI2OTY3NDk2ZTc0NTU2ZTczNjk2NzZlNjU2NDAwMDIwMzY1NmU3NjEzNmQ0Mjc1NjY2NjY1NzI1Mzc0NmY3MjYxNjc2NTUzNzQ2ZjcyNjUwMDAyMDM2NTZlNzYwZjZkNDI3NTY2NjY2NTcyNTM2NTc0NDI3OTc0NjU3MzAwMDUwMzY1NmU3NjBlNjM2ODY1NjM2YjRlNmY1MDYxNzk2ZDY1NmU3NDAwMDAwMzY1NmU3NjE0NjI2OTY3NDk2ZTc0NDY2OTZlNjk3MzY4NTU2ZTczNjk2NzZlNjU2NDAwMDQwMzY1NmU3NjA5NjI2OTY3NDk2ZTc0NDE2NDY0MDAwNjAzMGIwYTAxMDEwNDA3MDMwMTAwMDAwMDAwMDUwMzAxMDAwMzA2MGYwMjdmMDA0MWEwODAwODBiN2YwMDQxYTA4MDA4MGIwNzQ2MDcwNjZkNjU2ZDZmNzI3OTAyMDAwNDY5NmU2OTc0MDAxMTA2Njc2NTc0NTM3NTZkMDAxMjAzNjE2NDY0MDAxMzA4NjM2MTZjNmM0MjYxNjM2YjAwMTQwYTVmNWY2NDYxNzQ2MTVmNjU2ZTY0MDMwMDBiNWY1ZjY4NjU2MTcwNWY2MjYxNzM2NTAzMDEwYWNhMDEwYTBlMDEwMTdmNDEwMDEwMGMyMjAwMTAwMDIwMDAwYjE5MDEwMTdmNDE5YzgwMDg0MTljODAwODI4MDIwMDQxMDE2YjIyMDAzNjAyMDAyMDAwMGIxNDAwMTAwMTIwMDA0NjA0NDAwZjBiNDE4MDgwMDg0MTE5MTAwMjAwMGIxNjAwMjAwMDEwMGMyMjAwMTAwMzFhMjAwMDEwMGMyMjAwMTAwNDFhMjAwMDBiMTQwMTAxN2YxMDBjMjIwMjIwMDExMDA1MWEyMDAwMjAwMjEwMDYxYTBiMTMwMTAxN2YxMDBjMjIwMDQxOTk4MDA4NDEwMzEwMDcxYTIwMDAwYjE0MDEwMTdmMTAwODQxMDExMDBkMTAwYjIxMDAxMDEwMjAwMDEwMGYwYjBlMDAxMDA4NDEwMDEwMGQxMDEwMTAwZTEwMDkwYjIyMDEwMzdmMTAwODQxMDExMDBkMTAwYjIxMDExMDEwMjIwMjEwMGUyMjAwMjAwMDIwMDExMDBhMjAwMjIwMDAxMDBmMGIwMzAwMDEwYjBiMmYwMjAwNDE4MDgwMDgwYjFjNzc3MjZmNmU2NzIwNmU3NTZkNjI2NTcyMjA2ZjY2MjA2MTcyNjc3NTZkNjU2ZTc0NzM3Mzc1NmQwMDQxOWM4MDA4MGIwNDljZmZmZmZmQDA1MDBAMDUwNkAyYQ==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'MDA2MTczNmQwMTAwMDAwMDAxMjkwODYwMDAwMDYwMDAwMTdmNjAwMjdmN2YwMTdmNjAwMjdmN2YwMDYwMDE3ZjAwNjAwMzdmN2Y3ZjAxN2Y2MDAzN2Y3ZjdmMDA2MDAxN2YwMTdmMDI5MDAyMGIwMzY1NmU3NjE5NjI2OTY3NDk2ZTc0NDc2NTc0NTU2ZTczNjk2NzZlNjU2NDQxNzI2Nzc1NmQ2NTZlNzQwMDAzMDM2NTZlNzYwZjY3NjU3NDRlNzU2ZDQxNzI2Nzc1NmQ2NTZlNzQ3MzAwMDEwMzY1NmU3NjBiNzM2OTY3NmU2MTZjNDU3MjcyNmY3MjAwMDMwMzY1NmU3NjEyNmQ0Mjc1NjY2NjY1NzI1Mzc0NmY3MjYxNjc2NTRjNmY2MTY0MDAwMjAzNjU2ZTc2MTc2ZDQyNzU2NjY2NjU3MjU0NmY0MjY5Njc0OTZlNzQ1NTZlNzM2OTY3NmU2NTY0MDAwMjAzNjU2ZTc2MTk2ZDQyNzU2NjY2NjU3MjQ2NzI2ZjZkNDI2OTY3NDk2ZTc0NTU2ZTczNjk2NzZlNjU2NDAwMDIwMzY1NmU3NjEzNmQ0Mjc1NjY2NjY1NzI1Mzc0NmY3MjYxNjc2NTUzNzQ2ZjcyNjUwMDAyMDM2NTZlNzYwZjZkNDI3NTY2NjY2NTcyNTM2NTc0NDI3OTc0NjU3MzAwMDUwMzY1NmU3NjBlNjM2ODY1NjM2YjRlNmY1MDYxNzk2ZDY1NmU3NDAwMDAwMzY1NmU3NjE0NjI2OTY3NDk2ZTc0NDY2OTZlNjk3MzY4NTU2ZTczNjk2NzZlNjU2NDAwMDQwMzY1NmU3NjA5NjI2OTY3NDk2ZTc0NDE2NDY0MDAwNjAzMGIwYTAxMDEwNDA3MDMwMTAwMDAwMDAwMDUwMzAxMDAwMzA2MGYwMjdmMDA0MWEwODAwODBiN2YwMDQxYTA4MDA4MGIwNzQ2MDcwNjZkNjU2ZDZmNzI3OTAyMDAwNDY5NmU2OTc0MDAxMTA2Njc2NTc0NTM3NTZkMDAxMjAzNjE2NDY0MDAxMzA4NjM2MTZjNmM0MjYxNjM2YjAwMTQwYTVmNWY2NDYxNzQ2MTVmNjU2ZTY0MDMwMDBiNWY1ZjY4NjU2MTcwNWY2MjYxNzM2NTAzMDEwYWNhMDEwYTBlMDEwMTdmNDEwMDEwMGMyMjAwMTAwMDIwMDAwYjE5MDEwMTdmNDE5YzgwMDg0MTljODAwODI4MDIwMDQxMDE2YjIyMDAzNjAyMDAyMDAwMGIxNDAwMTAwMTIwMDA0NjA0NDAwZjBiNDE4MDgwMDg0MTE5MTAwMjAwMGIxNjAwMjAwMDEwMGMyMjAwMTAwMzFhMjAwMDEwMGMyMjAwMTAwNDFhMjAwMDBiMTQwMTAxN2YxMDBjMjIwMjIwMDExMDA1MWEyMDAwMjAwMjEwMDYxYTBiMTMwMTAxN2YxMDBjMjIwMDQxOTk4MDA4NDEwMzEwMDcxYTIwMDAwYjE0MDEwMTdmMTAwODQxMDExMDBkMTAwYjIxMDAxMDEwMjAwMDEwMGYwYjBlMDAxMDA4NDEwMDEwMGQxMDEwMTAwZTEwMDkwYjIyMDEwMzdmMTAwODQxMDExMDBkMTAwYjIxMDExMDEwMjIwMjEwMGUyMjAwMjAwMDIwMDExMDBhMjAwMjIwMDAxMDBmMGIwMzAwMDEwYjBiMmYwMjAwNDE4MDgwMDgwYjFjNzc3MjZmNmU2NzIwNmU3NTZkNjI2NTcyMjA2ZjY2MjA2MTcyNjc3NTZkNjU2ZTc0NzM3Mzc1NmQwMDQxOWM4MDA4MGIwNDljZmZmZmZmQDA1MDBAMDUwNkAyYQ==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'MDA2MTczNmQwMTAwMDAwMDAxMjkwODYwMDAwMDYwMDAwMTdmNjAwMjdmN2YwMTdmNjAwMjdmN2YwMDYwMDE3ZjAwNjAwMzdmN2Y3ZjAxN2Y2MDAzN2Y3ZjdmMDA2MDAxN2YwMTdmMDI5MDAyMGIwMzY1NmU3NjE5NjI2OTY3NDk2ZTc0NDc2NTc0NTU2ZTczNjk2NzZlNjU2NDQxNzI2Nzc1NmQ2NTZlNzQwMDAzMDM2NTZlNzYwZjY3NjU3NDRlNzU2ZDQxNzI2Nzc1NmQ2NTZlNzQ3MzAwMDEwMzY1NmU3NjBiNzM2OTY3NmU2MTZjNDU3MjcyNmY3MjAwMDMwMzY1NmU3NjEyNmQ0Mjc1NjY2NjY1NzI1Mzc0NmY3MjYxNjc2NTRjNmY2MTY0MDAwMjAzNjU2ZTc2MTc2ZDQyNzU2NjY2NjU3MjU0NmY0MjY5Njc0OTZlNzQ1NTZlNzM2OTY3NmU2NTY0MDAwMjAzNjU2ZTc2MTk2ZDQyNzU2NjY2NjU3MjQ2NzI2ZjZkNDI2OTY3NDk2ZTc0NTU2ZTczNjk2NzZlNjU2NDAwMDIwMzY1NmU3NjEzNmQ0Mjc1NjY2NjY1NzI1Mzc0NmY3MjYxNjc2NTUzNzQ2ZjcyNjUwMDAyMDM2NTZlNzYwZjZkNDI3NTY2NjY2NTcyNTM2NTc0NDI3OTc0NjU3MzAwMDUwMzY1NmU3NjBlNjM2ODY1NjM2YjRlNmY1MDYxNzk2ZDY1NmU3NDAwMDAwMzY1NmU3NjE0NjI2OTY3NDk2ZTc0NDY2OTZlNjk3MzY4NTU2ZTczNjk2NzZlNjU2NDAwMDQwMzY1NmU3NjA5NjI2OTY3NDk2ZTc0NDE2NDY0MDAwNjAzMGIwYTAxMDEwNDA3MDMwMTAwMDAwMDAwMDUwMzAxMDAwMzA2MGYwMjdmMDA0MWEwODAwODBiN2YwMDQxYTA4MDA4MGIwNzQ2MDcwNjZkNjU2ZDZmNzI3OTAyMDAwNDY5NmU2OTc0MDAxMTA2Njc2NTc0NTM3NTZkMDAxMjAzNjE2NDY0MDAxMzA4NjM2MTZjNmM0MjYxNjM2YjAwMTQwYTVmNWY2NDYxNzQ2MTVmNjU2ZTY0MDMwMDBiNWY1ZjY4NjU2MTcwNWY2MjYxNzM2NTAzMDEwYWNhMDEwYTBlMDEwMTdmNDEwMDEwMGMyMjAwMTAwMDIwMDAwYjE5MDEwMTdmNDE5YzgwMDg0MTljODAwODI4MDIwMDQxMDE2YjIyMDAzNjAyMDAyMDAwMGIxNDAwMTAwMTIwMDA0NjA0NDAwZjBiNDE4MDgwMDg0MTE5MTAwMjAwMGIxNjAwMjAwMDEwMGMyMjAwMTAwMzFhMjAwMDEwMGMyMjAwMTAwNDFhMjAwMDBiMTQwMTAxN2YxMDBjMjIwMjIwMDExMDA1MWEyMDAwMjAwMjEwMDYxYTBiMTMwMTAxN2YxMDBjMjIwMDQxOTk4MDA4NDEwMzEwMDcxYTIwMDAwYjE0MDEwMTdmMTAwODQxMDExMDBkMTAwYjIxMDAxMDEwMjAwMDEwMGYwYjBlMDAxMDA4NDEwMDEwMGQxMDEwMTAwZTEwMDkwYjIyMDEwMzdmMTAwODQxMDExMDBkMTAwYjIxMDExMDEwMjIwMjEwMGUyMjAwMjAwMDIwMDExMDBhMjAwMjIwMDAxMDBmMGIwMzAwMDEwYjBiMmYwMjAwNDE4MDgwMDgwYjFjNzc3MjZmNmU2NzIwNmU3NTZkNjI2NTcyMjA2ZjY2MjA2MTcyNjc3NTZkNjU2ZTc0NzM3Mzc1NmQwMDQxOWM4MDA4MGIwNDljZmZmZmZmQDA1MDBAMDUwNkAyYQ==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: 0061736d010000000129086000006000017f60027f7f017f60027f7f0060017f0060037f7f7f017f60037f7f7f0060017f017f0290020b03656e7619626967496e74476574556e7369676e6564417267756d656e74000303656e760f6765744e756d417267756d656e7473000103656e760b7369676e616c4572726f72000303656e76126d42756666657253746f726167654c6f6164000203656e76176d427566666572546f426967496e74556e7369676e6564000203656e76196d42756666657246726f6d426967496e74556e7369676e6564000203656e76136d42756666657253746f7261676553746f7265000203656e760f6d4275666665725365744279746573000503656e760e636865636b4e6f5061796d656e74000003656e7614626967496e7446696e697368556e7369676e6564000403656e7609626967496e744164640006030b0a010104070301000000000503010003060f027f0041a080080b7f0041a080080b074607066d656d6f7279020004696e697400110667657453756d00120361646400130863616c6c4261636b00140a5f5f646174615f656e6403000b5f5f686561705f6261736503010aca010a0e01017f4100100c2200100020000b1901017f419c8008419c800828020041016b220036020020000b1400100120004604400f0b4180800841191002000b16002000100c220010031a2000100c220010041a20000b1401017f100c2202200110051a2000200210061a0b1301017f100c220041998008410310071a20000b1401017f10084101100d100b210010102000100f0b0e0010084100100d1010100e10090b2201037f10084101100d100b210110102202100e220020002001100a20022000100f0b0300010b0b2f0200418080080b1c77726f6e67206e756d626572206f6620617267756d656e747373756d00419c80080b049cffffff@0500@0506@2a\n" ] } @@ -1085,7 +1017,7 @@ " is_payable_by_sc=True\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(deploy_transaction))\n", + "print(\"Transaction:\", deploy_transaction.to_dictionary())\n", "print(\"Transaction data:\", deploy_transaction.data.decode())" ] }, @@ -1125,7 +1057,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -1141,7 +1073,7 @@ "\n", "address_computer = AddressComputer()\n", "contract_address = address_computer.compute_contract_address(\n", - " deployer=Address.new_from_bech32(deploy_transaction.sender),\n", + " deployer=deploy_transaction.sender,\n", " deployment_nonce=deploy_transaction.nonce\n", ")\n", "\n", @@ -1159,32 +1091,29 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In the end, you can parse the results using a `SmartContractTransactionsOutcomeParser`. However, since the `parse_deploy` method requires a `TransactionOutcome` object as input, we need to first convert our `TransactionOnNetwork` object to a `TransactionOutcome`, by means of a `TransactionsConverter`." + "In the end, you can parse the results using a `SmartContractTransactionsOutcomeParser`." ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "SmartContractDeployOutcome(return_code='', return_message='', contracts=[DeployedSmartContract(address=erd1qqqqqqqqqqqqqpgqws44xjx2t056nn79fn29q0rjwfrd3m43396ql35kxy, owner_address=erd1testnlersh4z0wsv8kjx39me4rmnvjkwu8dsaea7ukdvvc9z396qykv7z7, code_hash=384b680df7a95ebceca02ffb3e760a2fc288dea1b802685ef15df22ae88ba15b)])\n" + "SmartContractDeployOutcome(return_code='ok', return_message='ok', contracts=[DeployedSmartContract(address=erd1qqqqqqqqqqqqqpgqws44xjx2t056nn79fn29q0rjwfrd3m43396ql35kxy, owner_address=erd1testnlersh4z0wsv8kjx39me4rmnvjkwu8dsaea7ukdvvc9z396qykv7z7, code_hash=384b680df7a95ebceca02ffb3e760a2fc288dea1b802685ef15df22ae88ba15b)])\n" ] } ], "source": [ - "from multiversx_sdk import (SmartContractTransactionsOutcomeParser,\n", - " TransactionsConverter)\n", + "from multiversx_sdk import SmartContractTransactionsOutcomeParser\n", "\n", - "converter = TransactionsConverter()\n", "parser = SmartContractTransactionsOutcomeParser()\n", "\n", "transaction_on_network = proxy.get_transaction(\"0a7da74038244790b5bd4cd614c26cd5a6be76a6fcfcfb037974cc116b2ee9c6\")\n", - "transaction_outcome = converter.transaction_on_network_to_outcome(transaction_on_network)\n", - "parsed_outcome = parser.parse_deploy(transaction_outcome)\n", + "parsed_outcome = parser.parse_deploy(transaction_on_network)\n", "\n", "print(parsed_outcome)" ] @@ -1205,20 +1134,23 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'dXBncmFkZUNvbnRyYWN0QDAwNjE3MzZkMDEwMDAwMDAwMTI5MDg2MDAwMDA2MDAwMDE3ZjYwMDI3ZjdmMDE3ZjYwMDI3ZjdmMDA2MDAxN2YwMDYwMDM3ZjdmN2YwMTdmNjAwMzdmN2Y3ZjAwNjAwMTdmMDE3ZjAyOTAwMjBiMDM2NTZlNzYxOTYyNjk2NzQ5NmU3NDQ3NjU3NDU1NmU3MzY5Njc2ZTY1NjQ0MTcyNjc3NTZkNjU2ZTc0MDAwMzAzNjU2ZTc2MGY2NzY1NzQ0ZTc1NmQ0MTcyNjc3NTZkNjU2ZTc0NzMwMDAxMDM2NTZlNzYwYjczNjk2NzZlNjE2YzQ1NzI3MjZmNzIwMDAzMDM2NTZlNzYxMjZkNDI3NTY2NjY2NTcyNTM3NDZmNzI2MTY3NjU0YzZmNjE2NDAwMDIwMzY1NmU3NjE3NmQ0Mjc1NjY2NjY1NzI1NDZmNDI2OTY3NDk2ZTc0NTU2ZTczNjk2NzZlNjU2NDAwMDIwMzY1NmU3NjE5NmQ0Mjc1NjY2NjY1NzI0NjcyNmY2ZDQyNjk2NzQ5NmU3NDU1NmU3MzY5Njc2ZTY1NjQwMDAyMDM2NTZlNzYxMzZkNDI3NTY2NjY2NTcyNTM3NDZmNzI2MTY3NjU1Mzc0NmY3MjY1MDAwMjAzNjU2ZTc2MGY2ZDQyNzU2NjY2NjU3MjUzNjU3NDQyNzk3NDY1NzMwMDA1MDM2NTZlNzYwZTYzNjg2NTYzNmI0ZTZmNTA2MTc5NmQ2NTZlNzQwMDAwMDM2NTZlNzYxNDYyNjk2NzQ5NmU3NDQ2Njk2ZTY5NzM2ODU1NmU3MzY5Njc2ZTY1NjQwMDA0MDM2NTZlNzYwOTYyNjk2NzQ5NmU3NDQxNjQ2NDAwMDYwMzBiMGEwMTAxMDQwNzAzMDEwMDAwMDAwMDA1MDMwMTAwMDMwNjBmMDI3ZjAwNDFhMDgwMDgwYjdmMDA0MWEwODAwODBiMDc0NjA3MDY2ZDY1NmQ2ZjcyNzkwMjAwMDQ2OTZlNjk3NDAwMTEwNjY3NjU3NDUzNzU2ZDAwMTIwMzYxNjQ2NDAwMTMwODYzNjE2YzZjNDI2MTYzNmIwMDE0MGE1ZjVmNjQ2MTc0NjE1ZjY1NmU2NDAzMDAwYjVmNWY2ODY1NjE3MDVmNjI2MTczNjUwMzAxMGFjYTAxMGEwZTAxMDE3ZjQxMDAxMDBjMjIwMDEwMDAyMDAwMGIxOTAxMDE3ZjQxOWM4MDA4NDE5YzgwMDgyODAyMDA0MTAxNmIyMjAwMzYwMjAwMjAwMDBiMTQwMDEwMDEyMDAwNDYwNDQwMGYwYjQxODA4MDA4NDExOTEwMDIwMDBiMTYwMDIwMDAxMDBjMjIwMDEwMDMxYTIwMDAxMDBjMjIwMDEwMDQxYTIwMDAwYjE0MDEwMTdmMTAwYzIyMDIyMDAxMTAwNTFhMjAwMDIwMDIxMDA2MWEwYjEzMDEwMTdmMTAwYzIyMDA0MTk5ODAwODQxMDMxMDA3MWEyMDAwMGIxNDAxMDE3ZjEwMDg0MTAxMTAwZDEwMGIyMTAwMTAxMDIwMDAxMDBmMGIwZTAwMTAwODQxMDAxMDBkMTAxMDEwMGUxMDA5MGIyMjAxMDM3ZjEwMDg0MTAxMTAwZDEwMGIyMTAxMTAxMDIyMDIxMDBlMjIwMDIwMDAyMDAxMTAwYTIwMDIyMDAwMTAwZjBiMDMwMDAxMGIwYjJmMDIwMDQxODA4MDA4MGIxYzc3NzI2ZjZlNjcyMDZlNzU2ZDYyNjU3MjIwNmY2NjIwNjE3MjY3NzU2ZDY1NmU3NDczNzM3NTZkMDA0MTljODAwODBiMDQ5Y2ZmZmZmZkAwNTA2QDJh', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'dXBncmFkZUNvbnRyYWN0QDAwNjE3MzZkMDEwMDAwMDAwMTI5MDg2MDAwMDA2MDAwMDE3ZjYwMDI3ZjdmMDE3ZjYwMDI3ZjdmMDA2MDAxN2YwMDYwMDM3ZjdmN2YwMTdmNjAwMzdmN2Y3ZjAwNjAwMTdmMDE3ZjAyOTAwMjBiMDM2NTZlNzYxOTYyNjk2NzQ5NmU3NDQ3NjU3NDU1NmU3MzY5Njc2ZTY1NjQ0MTcyNjc3NTZkNjU2ZTc0MDAwMzAzNjU2ZTc2MGY2NzY1NzQ0ZTc1NmQ0MTcyNjc3NTZkNjU2ZTc0NzMwMDAxMDM2NTZlNzYwYjczNjk2NzZlNjE2YzQ1NzI3MjZmNzIwMDAzMDM2NTZlNzYxMjZkNDI3NTY2NjY2NTcyNTM3NDZmNzI2MTY3NjU0YzZmNjE2NDAwMDIwMzY1NmU3NjE3NmQ0Mjc1NjY2NjY1NzI1NDZmNDI2OTY3NDk2ZTc0NTU2ZTczNjk2NzZlNjU2NDAwMDIwMzY1NmU3NjE5NmQ0Mjc1NjY2NjY1NzI0NjcyNmY2ZDQyNjk2NzQ5NmU3NDU1NmU3MzY5Njc2ZTY1NjQwMDAyMDM2NTZlNzYxMzZkNDI3NTY2NjY2NTcyNTM3NDZmNzI2MTY3NjU1Mzc0NmY3MjY1MDAwMjAzNjU2ZTc2MGY2ZDQyNzU2NjY2NjU3MjUzNjU3NDQyNzk3NDY1NzMwMDA1MDM2NTZlNzYwZTYzNjg2NTYzNmI0ZTZmNTA2MTc5NmQ2NTZlNzQwMDAwMDM2NTZlNzYxNDYyNjk2NzQ5NmU3NDQ2Njk2ZTY5NzM2ODU1NmU3MzY5Njc2ZTY1NjQwMDA0MDM2NTZlNzYwOTYyNjk2NzQ5NmU3NDQxNjQ2NDAwMDYwMzBiMGEwMTAxMDQwNzAzMDEwMDAwMDAwMDA1MDMwMTAwMDMwNjBmMDI3ZjAwNDFhMDgwMDgwYjdmMDA0MWEwODAwODBiMDc0NjA3MDY2ZDY1NmQ2ZjcyNzkwMjAwMDQ2OTZlNjk3NDAwMTEwNjY3NjU3NDUzNzU2ZDAwMTIwMzYxNjQ2NDAwMTMwODYzNjE2YzZjNDI2MTYzNmIwMDE0MGE1ZjVmNjQ2MTc0NjE1ZjY1NmU2NDAzMDAwYjVmNWY2ODY1NjE3MDVmNjI2MTczNjUwMzAxMGFjYTAxMGEwZTAxMDE3ZjQxMDAxMDBjMjIwMDEwMDAyMDAwMGIxOTAxMDE3ZjQxOWM4MDA4NDE5YzgwMDgyODAyMDA0MTAxNmIyMjAwMzYwMjAwMjAwMDBiMTQwMDEwMDEyMDAwNDYwNDQwMGYwYjQxODA4MDA4NDExOTEwMDIwMDBiMTYwMDIwMDAxMDBjMjIwMDEwMDMxYTIwMDAxMDBjMjIwMDEwMDQxYTIwMDAwYjE0MDEwMTdmMTAwYzIyMDIyMDAxMTAwNTFhMjAwMDIwMDIxMDA2MWEwYjEzMDEwMTdmMTAwYzIyMDA0MTk5ODAwODQxMDMxMDA3MWEyMDAwMGIxNDAxMDE3ZjEwMDg0MTAxMTAwZDEwMGIyMTAwMTAxMDIwMDAxMDBmMGIwZTAwMTAwODQxMDAxMDBkMTAxMDEwMGUxMDA5MGIyMjAxMDM3ZjEwMDg0MTAxMTAwZDEwMGIyMTAxMTAxMDIyMDIxMDBlMjIwMDIwMDAyMDAxMTAwYTIwMDIyMDAwMTAwZjBiMDMwMDAxMGIwYjJmMDIwMDQxODA4MDA4MGIxYzc3NzI2ZjZlNjcyMDZlNzU2ZDYyNjU3MjIwNmY2NjIwNjE3MjY3NzU2ZDY1NmU3NDczNzM3NTZkMDA0MTljODAwODBiMDQ5Y2ZmZmZmZkAwNTA2QDJh', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'dXBncmFkZUNvbnRyYWN0QDAwNjE3MzZkMDEwMDAwMDAwMTI5MDg2MDAwMDA2MDAwMDE3ZjYwMDI3ZjdmMDE3ZjYwMDI3ZjdmMDA2MDAxN2YwMDYwMDM3ZjdmN2YwMTdmNjAwMzdmN2Y3ZjAwNjAwMTdmMDE3ZjAyOTAwMjBiMDM2NTZlNzYxOTYyNjk2NzQ5NmU3NDQ3NjU3NDU1NmU3MzY5Njc2ZTY1NjQ0MTcyNjc3NTZkNjU2ZTc0MDAwMzAzNjU2ZTc2MGY2NzY1NzQ0ZTc1NmQ0MTcyNjc3NTZkNjU2ZTc0NzMwMDAxMDM2NTZlNzYwYjczNjk2NzZlNjE2YzQ1NzI3MjZmNzIwMDAzMDM2NTZlNzYxMjZkNDI3NTY2NjY2NTcyNTM3NDZmNzI2MTY3NjU0YzZmNjE2NDAwMDIwMzY1NmU3NjE3NmQ0Mjc1NjY2NjY1NzI1NDZmNDI2OTY3NDk2ZTc0NTU2ZTczNjk2NzZlNjU2NDAwMDIwMzY1NmU3NjE5NmQ0Mjc1NjY2NjY1NzI0NjcyNmY2ZDQyNjk2NzQ5NmU3NDU1NmU3MzY5Njc2ZTY1NjQwMDAyMDM2NTZlNzYxMzZkNDI3NTY2NjY2NTcyNTM3NDZmNzI2MTY3NjU1Mzc0NmY3MjY1MDAwMjAzNjU2ZTc2MGY2ZDQyNzU2NjY2NjU3MjUzNjU3NDQyNzk3NDY1NzMwMDA1MDM2NTZlNzYwZTYzNjg2NTYzNmI0ZTZmNTA2MTc5NmQ2NTZlNzQwMDAwMDM2NTZlNzYxNDYyNjk2NzQ5NmU3NDQ2Njk2ZTY5NzM2ODU1NmU3MzY5Njc2ZTY1NjQwMDA0MDM2NTZlNzYwOTYyNjk2NzQ5NmU3NDQxNjQ2NDAwMDYwMzBiMGEwMTAxMDQwNzAzMDEwMDAwMDAwMDA1MDMwMTAwMDMwNjBmMDI3ZjAwNDFhMDgwMDgwYjdmMDA0MWEwODAwODBiMDc0NjA3MDY2ZDY1NmQ2ZjcyNzkwMjAwMDQ2OTZlNjk3NDAwMTEwNjY3NjU3NDUzNzU2ZDAwMTIwMzYxNjQ2NDAwMTMwODYzNjE2YzZjNDI2MTYzNmIwMDE0MGE1ZjVmNjQ2MTc0NjE1ZjY1NmU2NDAzMDAwYjVmNWY2ODY1NjE3MDVmNjI2MTczNjUwMzAxMGFjYTAxMGEwZTAxMDE3ZjQxMDAxMDBjMjIwMDEwMDAyMDAwMGIxOTAxMDE3ZjQxOWM4MDA4NDE5YzgwMDgyODAyMDA0MTAxNmIyMjAwMzYwMjAwMjAwMDBiMTQwMDEwMDEyMDAwNDYwNDQwMGYwYjQxODA4MDA4NDExOTEwMDIwMDBiMTYwMDIwMDAxMDBjMjIwMDEwMDMxYTIwMDAxMDBjMjIwMDEwMDQxYTIwMDAwYjE0MDEwMTdmMTAwYzIyMDIyMDAxMTAwNTFhMjAwMDIwMDIxMDA2MWEwYjEzMDEwMTdmMTAwYzIyMDA0MTk5ODAwODQxMDMxMDA3MWEyMDAwMGIxNDAxMDE3ZjEwMDg0MTAxMTAwZDEwMGIyMTAwMTAxMDIwMDAxMDBmMGIwZTAwMTAwODQxMDAxMDBkMTAxMDEwMGUxMDA5MGIyMjAxMDM3ZjEwMDg0MTAxMTAwZDEwMGIyMTAxMTAxMDIyMDIxMDBlMjIwMDIwMDAyMDAxMTAwYTIwMDIyMDAwMTAwZjBiMDMwMDAxMGIwYjJmMDIwMDQxODA4MDA4MGIxYzc3NzI2ZjZlNjcyMDZlNzU2ZDYyNjU3MjIwNmY2NjIwNjE3MjY3NzU2ZDY1NmU3NDczNzM3NTZkMDA0MTljODAwODBiMDQ5Y2ZmZmZmZkAwNTA2QDJh', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: upgradeContract@0061736d010000000129086000006000017f60027f7f017f60027f7f0060017f0060037f7f7f017f60037f7f7f0060017f017f0290020b03656e7619626967496e74476574556e7369676e6564417267756d656e74000303656e760f6765744e756d417267756d656e7473000103656e760b7369676e616c4572726f72000303656e76126d42756666657253746f726167654c6f6164000203656e76176d427566666572546f426967496e74556e7369676e6564000203656e76196d42756666657246726f6d426967496e74556e7369676e6564000203656e76136d42756666657253746f7261676553746f7265000203656e760f6d4275666665725365744279746573000503656e760e636865636b4e6f5061796d656e74000003656e7614626967496e7446696e697368556e7369676e6564000403656e7609626967496e744164640006030b0a010104070301000000000503010003060f027f0041a080080b7f0041a080080b074607066d656d6f7279020004696e697400110667657453756d00120361646400130863616c6c4261636b00140a5f5f646174615f656e6403000b5f5f686561705f6261736503010aca010a0e01017f4100100c2200100020000b1901017f419c8008419c800828020041016b220036020020000b1400100120004604400f0b4180800841191002000b16002000100c220010031a2000100c220010041a20000b1401017f100c2202200110051a2000200210061a0b1301017f100c220041998008410310071a20000b1401017f10084101100d100b210010102000100f0b0e0010084100100d1010100e10090b2201037f10084101100d100b210110102202100e220020002001100a20022000100f0b0300010b0b2f0200418080080b1c77726f6e67206e756d626572206f6620617267756d656e747373756d00419c80080b049cffffff@0506@2a\n" ] } ], "source": [ + "from pathlib import Path\n", + "\n", + "from multiversx_sdk import Address\n", + "\n", "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm\")\n", "bytecode = Path(\"./contracts/adder.wasm\").read_bytes()\n", "\n", @@ -1234,7 +1166,7 @@ " is_payable_by_sc=True\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(upgrade_transaction))\n", + "print(\"Transaction:\", upgrade_transaction.to_dictionary())\n", "print(\"Transaction data:\", upgrade_transaction.data.decode())" ] }, @@ -1254,12 +1186,13 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ - "from multiversx_sdk import SmartContractTransactionsFactory\n", + "from multiversx_sdk import SmartContractTransactionsFactory, TransactionsFactoryConfig\n", "\n", + "config = TransactionsFactoryConfig(chain_id=\"D\")\n", "factory = SmartContractTransactionsFactory(config)" ] }, @@ -1272,7 +1205,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -1296,20 +1229,22 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qqqqqqqqqqqqqpgqws44xjx2t056nn79fn29q0rjwfrd3m43396ql35kxy', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'YWRkQDJh', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qqqqqqqqqqqqqpgqws44xjx2t056nn79fn29q0rjwfrd3m43396ql35kxy', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'YWRkQDJh', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qqqqqqqqqqqqqpgqws44xjx2t056nn79fn29q0rjwfrd3m43396ql35kxy', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'YWRkQDJh', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: add@2a\n" ] } ], "source": [ + "from multiversx_sdk import Address\n", + "from multiversx_sdk.abi import U32Value\n", + "\n", "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgqws44xjx2t056nn79fn29q0rjwfrd3m43396ql35kxy\")\n", "\n", "# For arguments, use typed value objects if you haven't provided an ABI to the factory:\n", @@ -1325,7 +1260,7 @@ " arguments=args\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(transaction))\n", + "print(\"Transaction:\", transaction.to_dictionary())\n", "print(\"Transaction data:\", transaction.data.decode())" ] }, @@ -1367,7 +1302,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -1392,20 +1327,21 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'TXVsdGlFU0RUTkZUVHJhbnNmZXJAMDAwMDAwMDAwMDAwMDAwMDA1MDA3NDJiNTM0OGNhNWJlOWE5Y2ZjNTRjZDQ1MDNjNzI3MjQ2ZDhlZWIxODk3NEAwMkA1NDQ1NTM1NDJkMzMzODY2MzIzNDM5QDAxQDAxQDQyNDE1MjJkNjMzODMwNjQzMjM5QEA4YWM3MjMwNDg5ZTgwMDAwQDYxNjQ2NEAyYQ==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", - "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'TXVsdGlFU0RUTkZUVHJhbnNmZXJAMDAwMDAwMDAwMDAwMDAwMDA1MDA3NDJiNTM0OGNhNWJlOWE5Y2ZjNTRjZDQ1MDNjNzI3MjQ2ZDhlZWIxODk3NEAwMkA1NDQ1NTM1NDJkMzMzODY2MzIzNDM5QDAxQDAxQDQyNDE1MjJkNjMzODMwNjQzMjM5QEA4YWM3MjMwNDg5ZTgwMDAwQDYxNjQ2NEAyYQ==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': '', 'relayer': '', 'innerTransactions': []}\n", + "Transaction: {'nonce': 0, 'value': '0', 'receiver': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'sender': 'erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th', 'senderUsername': '', 'receiverUsername': '', 'gasPrice': 1000000000, 'gasLimit': 10000000, 'data': 'TXVsdGlFU0RUTkZUVHJhbnNmZXJAMDAwMDAwMDAwMDAwMDAwMDA1MDA3NDJiNTM0OGNhNWJlOWE5Y2ZjNTRjZDQ1MDNjNzI3MjQ2ZDhlZWIxODk3NEAwMkA1NDQ1NTM1NDJkMzMzODY2MzIzNDM5QDAxQDAxQDQyNDE1MjJkNjMzODMwNjQzMjM5QEA4YWM3MjMwNDg5ZTgwMDAwQDYxNjQ2NEAyYQ==', 'chainID': 'D', 'version': 2, 'options': 0, 'guardian': '', 'signature': '', 'guardianSignature': ''}\n", "Transaction data: MultiESDTNFTTransfer@00000000000000000500742b5348ca5be9a9cfc54cd4503c727246d8eeb18974@02@544553542d333866323439@01@01@4241522d633830643239@@8ac7230489e80000@616464@2a\n" ] } ], "source": [ + "from multiversx_sdk import Token, TokenTransfer\n", + "\n", "first_token = Token(\"TEST-38f249\", 1)\n", "first_transfer = TokenTransfer(first_token, 1)\n", "\n", @@ -1423,7 +1359,7 @@ " token_transfers=transfers\n", ")\n", "\n", - "print(\"Transaction:\", transaction_converter.transaction_to_dictionary(transaction))\n", + "print(\"Transaction:\", transaction.to_dictionary())\n", "print(\"Transaction data:\", transaction.data.decode())" ] }, @@ -1451,22 +1387,19 @@ "\n", "Suppose we'd like to decode a `startPerformAction` event emitted by the [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract.\n", "\n", - "Let's fetch an already processed [transaction](https://devnet-explorer.multiversx.com/transactions/05d445cdd145ecb20374844dcc67f0b1e370b9aa28a47492402bc1a150c2bab4), to serve as an exmple, and convert it to a [TransactionOutcome](https://multiversx.github.io/mx-sdk-py/_modules/multiversx_sdk/core/transactions_outcome_parsers/resources.html#TransactionOutcome)." + "Let's fetch an already processed [transaction](https://devnet-explorer.multiversx.com/transactions/05d445cdd145ecb20374844dcc67f0b1e370b9aa28a47492402bc1a150c2bab4), to serve as an exmple." ] }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ - "from multiversx_sdk import ApiNetworkProvider, TransactionsConverter\n", - "\n", - "api = ApiNetworkProvider(\"https://testnet-api.multiversx.com\")\n", - "converter = TransactionsConverter()\n", + "from multiversx_sdk import ApiNetworkProvider\n", "\n", - "transaction_on_network = api.get_transaction(\"6f006c99e45525c94629db2442d9ca27ff088ad113a09f0a3a3e24bcc164945a\")\n", - "transaction_outcome = converter.transaction_on_network_to_outcome(transaction_on_network)" + "api = ApiNetworkProvider(\"https://devnet-api.multiversx.com\")\n", + "transaction_on_network = api.get_transaction(\"6651b983d494d69d94ce3efb3ae1604480af7c17780ab58daa09a9e5cc1d86c8\")" ] }, { @@ -1478,14 +1411,14 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 45, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "namespace(data=namespace(action_id=1, group_id=0, action_data=namespace(0=b'\\x019G.\\xffh\\x86w\\x1a\\x98/0\\x83\\xda]B\\x1f$\\xc2\\x91\\x81\\xe68\\x88\"\\x8d\\xc8\\x1c\\xa6\\ri\\xe1', __discriminant__=1), signers=[b'<5\\xd7\\xea\\xcb\\x1a\\x99\\xa4\\xa0\\xcc\\xaaS~\\xf0\\xcc\\n\\xe1\\t\\xf4\\x1fl+@\"\\x03\\xe63\\xbf\\xbb\\x07\\x1f\\\\']))\n" + "namespace(data=namespace(action_id=1, group_id=0, action_data=_EnumPayload(0=namespace(to=b'\\x1e\\x8a\\x8bkI\\xde[{\\xe1\\n\\xaa\\x15\\x8aZjJ\\xbbKV\\xcc\\x08\\xf5$\\xbb^l\\xd5\\xf2\\x11\\xad>\\x13', egld_amount=1000000000000000000, opt_gas_limit=None, endpoint_name=b'', arguments=[]), __discriminant__=5), signers=[b'\\x019G.\\xffh\\x86w\\x1a\\x98/0\\x83\\xda]B\\x1f$\\xc2\\x91\\x81\\xe68\\x88\"\\x8d\\xc8\\x1c\\xa6\\ri\\xe1', b'\\x80I\\xd69\\xe5\\xa6\\x98\\r\\x1c\\xd29*\\xbc\\xceA\\x02\\x9c\\xdat\\xa1V5#\\xa2\\x02\\xf0\\x96A\\xcc&\\x18\\xf8']))\n" ] } ], @@ -1496,7 +1429,7 @@ "abi = Abi.load(Path(\"./contracts/multisig-full.abi.json\"))\n", "events_parser = TransactionEventsParser(abi)\n", "\n", - "[event] = find_events_by_first_topic(transaction_outcome, \"startPerformAction\")\n", + "[event] = find_events_by_first_topic(transaction_on_network, \"startPerformAction\")\n", "parsed_event = events_parser.parse_event(event)\n", "\n", "print(parsed_event)" @@ -1515,7 +1448,7 @@ "source": [ "In order to perform Smart Contract queries, we recommend the use of `SmartContractQueriesController`.\n", "\n", - "You will notice that the `SmartContractQueriesController` requires a `QueryRunner` object at initialization. A `NetworkProvider`, slighly adapted, is used to satisfy this requirement (more details about **network providers** can be found in a later section)." + "You will notice that the `SmartContractQueriesController` requires a `NetworkProvider` object at initialization." ] }, { @@ -1524,13 +1457,10 @@ "metadata": {}, "outputs": [], "source": [ - "from multiversx_sdk import (ProxyNetworkProvider, QueryRunnerAdapter,\n", - " SmartContractQueriesController)\n", + "from multiversx_sdk import ApiNetworkProvider, SmartContractQueriesController\n", "\n", - "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgqqy34h7he2ya6qcagqre7ur7cc65vt0mxrc8qnudkr4\")\n", - "query_runner = QueryRunnerAdapter(ProxyNetworkProvider(\"https://devnet-api.multiversx.com\"))\n", - "\n", - "query_controller = SmartContractQueriesController(query_runner)\n" + "api = ApiNetworkProvider(\"https://devnet-api.multiversx.com\")\n", + "query_controller = SmartContractQueriesController(network_provider=api)\n" ] }, { @@ -1547,7 +1477,7 @@ "outputs": [], "source": [ "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", - "query_controller = SmartContractQueriesController(query_runner, abi)" + "query_controller = SmartContractQueriesController(network_provider=api, abi=abi)" ] }, { @@ -1571,6 +1501,10 @@ } ], "source": [ + "from multiversx_sdk import Address\n", + "\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgqqy34h7he2ya6qcagqre7ur7cc65vt0mxrc8qnudkr4\")\n", + "\n", "data_parts = query_controller.query(\n", " contract=contract.to_bech32(),\n", " function=\"getSum\",\n", @@ -1634,7 +1568,6 @@ { "cell_type": "code", "execution_count": 43, - "execution_count": 43, "metadata": {}, "outputs": [ { @@ -1665,7 +1598,6 @@ { "cell_type": "code", "execution_count": 44, - "execution_count": 44, "metadata": {}, "outputs": [], "source": [ @@ -1690,7 +1622,6 @@ { "cell_type": "code", "execution_count": 45, - "execution_count": 45, "metadata": {}, "outputs": [ { @@ -1721,7 +1652,6 @@ { "cell_type": "code", "execution_count": 46, - "execution_count": 46, "metadata": {}, "outputs": [], "source": [ @@ -1743,7 +1673,6 @@ { "cell_type": "code", "execution_count": 47, - "execution_count": 47, "metadata": {}, "outputs": [], "source": [ @@ -1783,7 +1712,6 @@ { "cell_type": "code", "execution_count": 48, - "execution_count": 48, "metadata": {}, "outputs": [ { @@ -1816,7 +1744,6 @@ { "cell_type": "code", "execution_count": 49, - "execution_count": 49, "metadata": {}, "outputs": [ { @@ -1847,7 +1774,6 @@ { "cell_type": "code", "execution_count": 50, - "execution_count": 50, "metadata": {}, "outputs": [ { @@ -1885,7 +1811,6 @@ { "cell_type": "code", "execution_count": 51, - "execution_count": 51, "metadata": {}, "outputs": [], "source": [ @@ -1904,7 +1829,6 @@ { "cell_type": "code", "execution_count": 52, - "execution_count": 52, "metadata": {}, "outputs": [], "source": [ @@ -1920,25 +1844,24 @@ }, { "cell_type": "code", - "execution_count": 53, - "execution_count": 53, + "execution_count": 52, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Signature: ee6d665981c00258858923bbbe51e1cfe29911716190a3eedc24e2205100bb05faf3d8133fc973b00409aa18708b41488702d635efefe6a24b4a2b7f66267a00\n" + "Signature: 283d5dc0f5074f494c5e00a7fa6c9f337763c962e302f1b711033fa0113a3a62310021dfe8465e45e5f09db20d69caac942213215560e074f600fbbeb290900b\n" ] } ], "source": [ - "from multiversx_sdk import Transaction, TransactionComputer\n", + "from multiversx_sdk import Address, Transaction, TransactionComputer\n", "\n", "tx = Transaction(\n", " nonce=90,\n", - " sender=\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\",\n", - " receiver=\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\",\n", + " sender=Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\"),\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " value=1000000000000000000,\n", " gas_limit=50000,\n", " chain_id=\"D\"\n", @@ -1959,15 +1882,14 @@ }, { "cell_type": "code", - "execution_count": 54, - "execution_count": 54, + "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Signature: 561bc58f1dc6b10de208b2d2c22c9a474ea5e8cabb59c3d3ce06bbda21cc46454aa71a85d5a60442bd7784effa2e062fcb8fb421c521f898abf7f5ec165e5d0f\n" + "Signature: e77e6f77dd800d346f9ebe95a23c55eaaa74830fd1185cd57faa4b0372436f57cd3d5c3ac79f36ed7809ab61c08a82567179e56e9a5192ba5732160adaf5490a\n" ] } ], @@ -2000,7 +1922,6 @@ { "cell_type": "code", "execution_count": 55, - "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -2023,7 +1944,6 @@ { "cell_type": "code", "execution_count": 56, - "execution_count": 56, "metadata": {}, "outputs": [ { @@ -2072,8 +1992,7 @@ }, { "cell_type": "code", - "execution_count": 57, - "execution_count": 57, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -2091,8 +2010,7 @@ }, { "cell_type": "code", - "execution_count": 58, - "execution_count": 58, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -2101,6 +2019,25 @@ "provider = ProxyNetworkProvider(\"https://devnet-gateway.multiversx.com\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, we can create a `NetworkProviderConfig` to set an User-Agent or to specify custom request headers." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import NetworkProviderConfig\n", + "\n", + "config = NetworkProviderConfig(client_name=\"py-examples\")\n", + "provider = ProxyNetworkProvider(url=\"https://devnet-gateway.multiversx.com\", config=config)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2117,8 +2054,7 @@ }, { "cell_type": "code", - "execution_count": 59, - "execution_count": 59, + "execution_count": 57, "metadata": {}, "outputs": [ { @@ -2153,16 +2089,15 @@ }, { "cell_type": "code", - "execution_count": 60, - "execution_count": 60, + "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Nonce: 1853\n", - "Balance: 3364970609086014407\n" + "Nonce: 10782\n", + "Balance: 45641510491204646308\n" ] } ], @@ -2173,35 +2108,6 @@ "print(\"Balance:\", account_on_network.balance)" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When sending a number of transactions, you usually have to first fetch the account nonce from the network (see above), then manage it locally (e.g. increment upon signing & broadcasting a transaction):" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "execution_count": 61, - "metadata": {}, - "outputs": [], - "source": [ - "from multiversx_sdk import AccountNonceHolder\n", - "\n", - "nonce_holder = AccountNonceHolder(account_on_network.nonce)\n", - "\n", - "tx.nonce = nonce_holder.get_nonce_then_increment()\n", - "# Then, sign & broadcast the transaction(s)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For further reference, please see [nonce management](https://docs.multiversx.com/integrators/creating-transactions/#nonce-management)." - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -2218,35 +2124,40 @@ }, { "cell_type": "code", - "execution_count": 62, - "execution_count": 62, + "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Transaction hash: 999da2b84011ffd6aedb966c8262804bb5280064a77f543f841e4eae96af8013\n" + "Transaction hash: 53f98ae90fef48a028af3122c36859a9073a5052155ce22113963ab0f71f2670\n" ] } ], "source": [ + "from multiversx_sdk import Address, NetworkProviderConfig, ProxyNetworkProvider, Transaction, UserSigner\n", + "\n", + "config = NetworkProviderConfig(client_name=\"py-examples\")\n", + "provider = ProxyNetworkProvider(url=\"https://devnet-gateway.multiversx.com\", config=config)\n", + "\n", "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", "\n", "tx = Transaction(\n", - " sender=alice.to_bech32(),\n", - " receiver=alice.to_bech32(),\n", + " sender=alice,\n", + " receiver=alice,\n", " gas_limit=50000,\n", " chain_id=\"D\"\n", ")\n", "\n", "alice_on_network = provider.get_account(alice)\n", - "\n", "tx.nonce = alice_on_network.nonce\n", + "\n", + "signer = UserSigner.from_pem_file(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "tx.signature = signer.sign(transaction_computer.compute_bytes_for_signing(tx))\n", "\n", "hash = provider.send_transaction(tx)\n", - "print(\"Transaction hash:\", hash)" + "print(\"Transaction hash:\", hash.hex())" ] }, { @@ -2258,53 +2169,59 @@ }, { "cell_type": "code", - "execution_count": 63, - "execution_count": 63, + "execution_count": 68, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Transactions hashes: (3, {'0': '999da2b84011ffd6aedb966c8262804bb5280064a77f543f841e4eae96af8013', '1': '0cf69d20c599334047fb1ec9e392086a5b00a42b688dbd9728d7e979a2555622', '2': 'b2e2c5fbb4fbf9be8ec539af4cb719c5b478067eb6772fa0a47a031ef4150b5b'})\n" + "Transactions sent: 3\n", + "Transactions hashes: ['0e12db41401f6f1a5a024b7f2a8f3241da700b1c8a56a9672533981417f3710d', 'a557580df8cb8861c151c240f61b61de3c74502c60de8d4dfa89754c215c848f', '4fa8ea1747642b45063c63196565489845268ff466b327c24749c6462233f20a']\n" ] } ], "source": [ "tx_1 = Transaction(\n", - " sender=alice.to_bech32(),\n", - " receiver=alice.to_bech32(),\n", + " sender=alice,\n", + " receiver=alice,\n", " gas_limit=50000,\n", " chain_id=\"D\"\n", ")\n", "\n", "tx_2 = Transaction(\n", - " sender=alice.to_bech32(),\n", - " receiver=alice.to_bech32(),\n", + " sender=alice,\n", + " receiver=alice,\n", " gas_limit=50000,\n", " chain_id=\"D\"\n", ")\n", "\n", "tx_3 = Transaction(\n", - " sender=alice.to_bech32(),\n", - " receiver=alice.to_bech32(),\n", + " sender=alice,\n", + " receiver=alice,\n", " gas_limit=50000,\n", " chain_id=\"D\"\n", ")\n", "\n", "alice_on_network = provider.get_account(alice)\n", - "nonce_holder = AccountNonceHolder(account_on_network.nonce)\n", + "nonce = alice_on_network.nonce\n", + "\n", + "tx_1.nonce = nonce\n", + "nonce += 1\n", + "\n", + "tx_2.nonce = nonce\n", + "nonce += 1\n", "\n", - "tx_1.nonce = nonce_holder.get_nonce_then_increment()\n", - "tx_2.nonce = nonce_holder.get_nonce_then_increment()\n", - "tx_3.nonce = nonce_holder.get_nonce_then_increment()\n", + "tx_3.nonce = nonce\n", + "nonce += 1\n", "\n", "tx_1.signature = signer.sign(transaction_computer.compute_bytes_for_signing(tx_1))\n", "tx_2.signature = signer.sign(transaction_computer.compute_bytes_for_signing(tx_2))\n", "tx_3.signature = signer.sign(transaction_computer.compute_bytes_for_signing(tx_3))\n", "\n", - "hashes = provider.send_transactions([tx_1, tx_2, tx_3])\n", - "print(\"Transactions hashes:\", hashes)" + "num_transactions_sent, hashes = provider.send_transactions([tx_1, tx_2, tx_3])\n", + "print(\"Transactions sent:\", num_transactions_sent)\n", + "print(\"Transactions hashes:\", [hash.hex() for hash in hashes])" ] }, { @@ -2316,21 +2233,20 @@ }, { "cell_type": "code", - "execution_count": 64, - "execution_count": 64, + "execution_count": 69, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Status: success\n", + "Status: TransactionStatus(status='success', is_completed=True, is_successful=True)\n", "Is completed: True\n" ] } ], "source": [ - "tx_on_network = provider.get_transaction(\"9270a6879b682a7b310c659f58b641ccdd5f083e5633669817130269e5b0939b\", with_process_status=True)\n", + "tx_on_network = provider.get_transaction(\"9270a6879b682a7b310c659f58b641ccdd5f083e5633669817130269e5b0939b\")\n", "print(\"Status:\", tx_on_network.status)\n", "print(\"Is completed:\", tx_on_network.is_completed)" ] @@ -2352,7 +2268,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" + "version": "3.12.3" }, "orig_nbformat": 4, "vscode": { From f8724accd84e5fa63be9cfc9c756afe6db64a81e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 28 Nov 2024 14:28:02 +0200 Subject: [PATCH 02/17] wip: cookbook update --- examples/v1.ipynb | 1376 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1376 insertions(+) create mode 100644 examples/v1.ipynb diff --git a/examples/v1.ipynb b/examples/v1.ipynb new file mode 100644 index 00000000..da655c78 --- /dev/null +++ b/examples/v1.ipynb @@ -0,0 +1,1376 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "\n", + "sys.path.append(\"..\") # Add the parent directory of 'examples' to the Python path" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an Entrypoint\n", + "\n", + "The Entrypoint represents a network client that makes the most common operations easily accessible. We have an entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint` and `LocalnetEntrypoint`. We are going to interact with the Devnet so we create an entrypoint as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we want to create an entrypoint that uses a third party api, we can do so as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint(url=\"https://custom-multiversx-devnet-api.com\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating KeyPairs\n", + "\n", + "...\n", + "\n", + "Maybe create keypair from entrypoint" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Calling the Faucet\n", + "\n", + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The entrypoint exposes a few methods to directly interact with the network, such as:\n", + "\n", + "\n", + "- `recall_account_nonce(self, address: Address) -> int;`\n", + "- `send_transaction(self, transaction: Transaction) -> bytes;`\n", + "- `send_transactions(self, transactions: list[Transaction]) -> tuple[int, list[bytes]];`\n", + "- `await_completed_transaction(self, tx_hash: Union[str, bytes]) -> TransactionOnNetwork;`\n", + "\n", + "Some other methods are exposed through a so called network provider. There are two types of network providers: ApiNetworkProvider and ProxyNetworkProvider. The ProxyNetworkProvider interacts directly with the proxy of an observing squad. The ApiNetworkProvider, as the name suggests, interacts with the api, that is a layer over the proxy. It fetches data from the network but also from the Elastic Search. By default, an Entrypoint, in our case the `DevnetEntrypoint`, uses the api, but we can also create a custom one that interacts with the proxy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "custom_entrypoint = DevnetEntrypoint(url=\"https:devnet-gateway.multiversx.com\", kind=\"proxy\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To get the underlying network provider from our entrypoint, we can do as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, when manually instantiating a network provider, a config can be provided to specify the client name and set custom request options." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import NetworkProviderConfig, ApiNetworkProvider\n", + "\n", + "config = NetworkProviderConfig(\n", + " client_name=\"hello-multiversx\",\n", + " requests_options={\n", + " \"timeout\": 1,\n", + " \"auth\": (\"user\", \"password\")\n", + " }\n", + ")\n", + "\n", + "api = ApiNetworkProvider(url=\"https://devnet-api.multiversx.com\", config=config)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A list of all the available methods from the `ApiNetworkProviders` can be found [here](https://multiversx.github.io/mx-sdk-py/multiversx_sdk.network_providers.html#module-multiversx_sdk.network_providers.api_network_provider).\n", + "\n", + "Both the `ApiNetworkProvider` and the `ProxyNetworkProvider` implement a common interface, that can be seen [here](https://github.com/multiversx/mx-sdk-py/blob/feat/next/multiversx_sdk/network_providers/interface.py#L16), so, the two network providers can be used interchangeably.\n", + "\n", + "The classes returned by the api have the most used fields easily accessible, but each object has a `raw` field where the raw api response is stored in case some other fields are needed." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fetching data from the network\n", + "\n", + "### Fetching the network config" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "network_config = api.get_network_config()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fetching the network status\n", + "\n", + "The status is fetched by default from the metachain, but a specific shard number can be provided." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "network_status = api.get_network_status() # fetches status from metachain\n", + "network_status = api.get_network_status(shard=1) # fetches status from shard 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fetching a block from the network\n", + "\n", + "We instantiate the args and we are going to fetch the block using it's hash. The `api` only supports fetching blocks by hash, while the `proxy` can fetch blocks by hash or by nonce. Keep in mind, that for the `proxy` the shard should also be specified in the arguments." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint, GetBlockArguments\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "args = GetBlockArguments(block_hash=bytes.fromhex(\"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"))\n", + "block = api.get_block(arguments=args)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, we can fetch the latest block from a specific shard:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint, GetBlockArguments\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "args = GetBlockArguments(block_hash=bytes.fromhex(\"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"))\n", + "latest_block = api.get_latest_block(shard=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fetching an account\n", + "\n", + "To fetch an account we'll need it's address. Once we have the address, we simply create an `Address` object and pass it as an argument to the method." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "account = api.get_account(address=alice)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fetching an account's storage\n", + "\n", + "We can also fetch an account's storage, which means we can get all the key-value pairs saved for an account." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "account = api.get_account_storage(address=alice)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we only want a specific key, we can fetch it as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "account = api.get_account_storage_entry(address=alice, entry_key=\"testKey\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Waiting for an account to satisfy a condition\n", + "\n", + "There might be situations when we want to wait for a certain condition to be satisfied in order to proceed with some action. For example, I want to send 7 EGLD from Alice to Bob, but I can do so only when Alice has a balance of minimum 7 EGLD. This can help in situations in which you are waiting for someone to send some funds to Alice so then she can send further transfer the funds. \n", + "\n", + "We need to define our condition that will be checked each time the account is fetched from the network. For this, we create a function that takes as an argument an `AccountOnNetwork` object and return a `bool`.\n", + "\n", + "Keep in mind that, this method has a default timeout that can be adjusted using the `AwaitingOptions` class." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, AccountOnNetwork, DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "def condition_to_be_satisfied(account: AccountOnNetwork) -> bool:\n", + " return account.balance >= 7000000000000000000 # 7 EGLD\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "account = api.await_account_on_condition(address=alice, condition=condition_to_be_satisfied)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sending and simulating transactions\n", + "\n", + "In order for our transactions to be executed, we use the network providers to broadcast them to the network. We'll discuss more about transactions in the dedicated section, but for now, we'll see minimal example. Keep in mind that, in order for transactions to be processed they need to be signed.\n", + "\n", + "#### Sending a transaction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint, Transaction\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "# this transaction is not signed; if this code is executed it will raise an error\n", + "transaction = Transaction(\n", + " sender=alice,\n", + " receiver=bob,\n", + " gas_limit=50000,\n", + " chain_id=\"D\"\n", + ")\n", + "\n", + "transaction_hash = api.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Simulating transactions\n", + "\n", + "A transaction can be simulated before being sent to be processed by the network. It is mostly used for smart contract calls to see what smart contract results are produced. We'll discuss more about transactions in the dedicated section, but for now, we'll see minimal example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint, Transaction\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn\")\n", + "\n", + "transaction = Transaction(\n", + " sender=alice,\n", + " receiver=contract,\n", + " gas_limit=5000000,\n", + " chain_id=\"D\",\n", + " data=b\"add@07\"\n", + ")\n", + "\n", + "transaction_hash = api.simulate_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Estimating the gas cost of a transaction\n", + "\n", + "Before sending a transaction to the network to be processed, one can get the estimated gas limit that is required for the transaction to be executed. We'll discuss more about transactions in the dedicated section, but for now, we'll see minimal example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint, Transaction\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn\")\n", + "\n", + "transaction = Transaction(\n", + " sender=alice,\n", + " receiver=contract,\n", + " gas_limit=5000000,\n", + " chain_id=\"D\",\n", + " data=b\"add@07\"\n", + ")\n", + "\n", + "transaction_hash = api.estimate_transaction_cost(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Waiting for transaction completion\n", + "\n", + "After sending a transaction, we may want to wait until the transaction is processed in order to proceed with another action. Keep in mind that, this method has a default timeout that can be adjusted using the `AwaitingOptions` class." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "tx_hash = \"exampletransactionhash\"\n", + "transaction_on_network = api.await_transaction_completed(transaction_hash=tx_hash)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Waiting for a transaction to specify a condition\n", + "\n", + "Similar to accounts, we can wait until a transaction satisfies a specific condition." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint, TransactionOnNetwork\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "# can be the creation of an event or something else\n", + "def condition_to_be_satisfied(transaction_on_network: TransactionOnNetwork) -> bool:\n", + " ...\n", + "\n", + "tx_hash = \"exampletransactionhash\"\n", + "transaction_on_network = api.await_transaction_on_condition(transaction_hash=tx_hash, condition=condition_to_be_satisfied)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fetching transactions from the network\n", + "\n", + "After sending transactions, we can fetch the transactions from the network. To do so, we need the transaction hash that we got after broadcasting the transaction." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "tx_hash = \"exampletransactionhash\"\n", + "transaction_on_network = api.get_transaction(tx_hash)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fetching a token from an account\n", + "\n", + "We can fetch a specific token (ESDT, MetaESDT, SFT, NFT) of an account by providing the address and the token." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint, Token\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "\n", + "token = Token(identifier=\"TEST-123456\") # ESDT\n", + "token_on_network = api.get_token_of_account(address=alice, token=token)\n", + "\n", + "token = Token(identifier=\"NFT-987654\", nonce=11) # NFT\n", + "token_on_network = api.get_token_of_account(address=alice, token=token)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fetching all fungible tokens of an account\n", + "\n", + "Fetches all fungible tokens held by an account. This method does not handle pagination, but can be achieved by using `do_get_generic`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "fungible_token = api.get_fungible_tokens_of_account(address=alice)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fetcing all non-fungible tokens of an account\n", + "\n", + "Fetches all non-fungible tokens held by an account. This method does not handle pagination, but can be achieved by using `do_get_generic`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "fungible_token = api.get_non_fungible_tokens_of_account(address=alice)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fetching token metadata\n", + "\n", + "If we want to fetch the metadata of a token, like `owner`, `decimals` and so on, we can use the following methods:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "# used for ESDT\n", + "fungible_token_definition = api.get_definition_of_fungible_token(token_identifier=\"TEST-123456\")\n", + "\n", + "# used for MetaESDT, SFT, NFT\n", + "non_fungible_token_definition = api.get_definition_of_tokens_collection(collection_name=\"NFT-987654\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Querying Smart Contracts\n", + "\n", + "Smart contract queries or view functions, are endpoints of a contract that only read data from the contract. We'll discuss more about smart contract queries in the Smart Contracts section, but for now, we use the network providers to send a query to the observer nodes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint, SmartContractQuery\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "query = SmartContractQuery(\n", + " contract=Address.new_from_bech32(\"\"),\n", + " function=\"testFunction\",\n", + " arguments=[]\n", + ")\n", + "response = api.query_contract(query=query)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Custom Api/Proxy calls\n", + "\n", + "The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and used ones. There might be times when custom api calls are needed. For that we have createad generic methods for both `GET` and `POST` requests.\n", + "\n", + "Let's assume we want to get all the transactions that are sent by Alice and call the `testFunction` function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "url_params = {\n", + " \"sender\": alice.to_bech32(),\n", + " \"function\": \"testFunction\"\n", + "}\n", + "\n", + "transactions = api.do_get_generic(url=\"transactions\", url_parameters=url_params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating transactions\n", + "\n", + "In this section, we'll learn how to create different types of transactions. For creating transactions, we can use `controllers` or `factories`. The `controllers` can be used for scripts or quick network interactions, while the `factories` provide a more granular approach. Usually, the `controllers` use the same parameters as the `factories` but also take an `Account` and the `nonce` of the sender as arguments. The `controllers` also hold some extra functionality, like waiting for transaction completion and parsing transactions. The same functionality can be obtained for `factories` as well, we'll see how in the sections bellow." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Instantiating controllers and factories\n", + "\n", + "There are two ways to create controllers and factories: the first one is to get them from the entrypoint and the second one is to manually create them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint, TransfersController, TransferTransactionsFactory, TransactionsFactoryConfig\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "\n", + "# getting the controller and the factory from the entrypoint\n", + "transfers_controller = entrypoint.create_transfers_controller()\n", + "transfers_factory = entrypoint.create_transfers_factory()\n", + "\n", + "# manually instantiating the controller and the factory\n", + "controller = TransfersController(chain_id=\"D\")\n", + "\n", + "config = TransactionsFactoryConfig(chain_id=\"D\")\n", + "factory = TransferTransactionsFactory(config=config)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Token transfers\n", + "\n", + "We can send native tokens (EGLD) and ESDT tokens using both the `controller` and the `factory`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Native token transfers\n", + "\n", + "First, we'll create the transaction using the controller. Because we'll use an `Account`, the transaction we'll be signed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "\n", + "account = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "account.nonce = entrypoint.recall_account_nonce(account.address)\n", + "\n", + "transfers_controller = entrypoint.create_transfers_controller()\n", + "transaction = transfers_controller.create_transaction_for_transfer(\n", + " sender=account,\n", + " nonce=account.get_nonce_then_increment(),\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " native_transfer_amount=1000000000000000000, # 1 EGLD\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's create the same transaction using the `factory`. Because we only use the address of the sender, the transactions are not going to be signed or have the nonce field set properly. This should be taken care after the transaction is created." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import TransferTransactionsFactory, TransactionsFactoryConfig\n", + "\n", + "config = TransactionsFactoryConfig(chain_id=\"D\")\n", + "factory = TransferTransactionsFactory(config=config)\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "transaction = factory.create_transaction_for_transfer(\n", + " sender=alice,\n", + " receiver=bob,\n", + " native_amount=1000000000000000000 # 1 EGLD\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Custom token transfers\n", + "\n", + "We'll use the same `controller` and `factory` to send custom tokens." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint, Token, TokenTransfer\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "\n", + "account = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "account.nonce = entrypoint.recall_account_nonce(account.address)\n", + "\n", + "esdt = Token(identifier=\"TEST-123456\")\n", + "first_transfer = TokenTransfer(token=esdt, amount=1000000000)\n", + "\n", + "nft = Token(identifier=\"NFT-987654\", nonce=10)\n", + "second_transfer = TokenTransfer(token=nft, amount=1)\n", + "\n", + "transfers_controller = entrypoint.create_transfers_controller()\n", + "transaction = transfers_controller.create_transaction_for_transfer(\n", + " sender=account,\n", + " nonce=account.get_nonce_then_increment(),\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " token_transfers=[first_transfer, second_transfer]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's create the same transaction using the `factory`. Because we only use the address of the sender, the transactions are not going to be signed or have the nonce field set properly. This should be taken care after the transaction is created." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import TransferTransactionsFactory, TransactionsFactoryConfig, Token, TokenTransfer\n", + "\n", + "config = TransactionsFactoryConfig(chain_id=\"D\")\n", + "factory = TransferTransactionsFactory(config=config)\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "esdt = Token(identifier=\"TEST-123456\")\n", + "first_transfer = TokenTransfer(token=esdt, amount=1000000000)\n", + "\n", + "nft = Token(identifier=\"NFT-987654\", nonce=10)\n", + "second_transfer = TokenTransfer(token=nft, amount=1)\n", + "\n", + "transaction = factory.create_transaction_for_transfer(\n", + " sender=alice,\n", + " receiver=bob,\n", + " token_transfers=[first_transfer, second_transfer]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Also, sending both native and custom tokens is now supported. We can send both types of tokens using either the `controller` or the `factory`, but we'll use the controller for the sake of simplicity." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint, Token, TokenTransfer\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "\n", + "account = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "account.nonce = entrypoint.recall_account_nonce(account.address)\n", + "\n", + "esdt = Token(identifier=\"TEST-123456\")\n", + "first_transfer = TokenTransfer(token=esdt, amount=1000000000)\n", + "\n", + "nft = Token(identifier=\"NFT-987654\", nonce=10)\n", + "second_transfer = TokenTransfer(token=nft, amount=1)\n", + "\n", + "transfers_controller = entrypoint.create_transfers_controller()\n", + "transaction = transfers_controller.create_transaction_for_transfer(\n", + " sender=account,\n", + " nonce=account.get_nonce_then_increment(),\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " native_transfer_amount=1000000000000000000, # 1 EGLD\n", + " token_transfers=[first_transfer, second_transfer]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Smart Contracts\n", + "\n", + "#### Contract ABIs\n", + "\n", + "A contract's ABI describes the endpoints, data structure and events that a contract exposes. While contract interactions are possible without the ABI, they are easier to implement when the definitions are available.\n", + "\n", + "##### Loading the ABI from a file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk.abi import Abi\n", + "\n", + "abi = Abi.load(Path(\"./contracts/adder.abi.json\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Manually construct the ABI\n", + "\n", + "If an ABI file isn't directly available, but you do have knowledge of the contract's endpoints and types, you can manually construct the ABI. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk.abi import Abi, AbiDefinition\n", + "\n", + "abi_definition = AbiDefinition.from_dict({\n", + " \"endpoints\": [{\n", + " \"name\": \"add\",\n", + " \"inputs\": [\n", + " {\n", + " \"name\": \"value\",\n", + " \"type\": \"BigUint\"\n", + " }\n", + " ],\n", + " \"outputs\": []\n", + " }]\n", + "})\n", + "\n", + "abi = Abi(definition=abi_definition)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Smart Contract deployments\n", + "\n", + "For creating smart contract deploy transactions, we have two options, as well: a `controler` and a `factory`. Both of these are similar to the ones presented above for transferring tokens.\n", + "\n", + "When creating transactions that interact with smart contracts, we should provide the ABI file to the `controller` or `factory` if possible, so we can pass the arguments as native values. If the abi is not provided and we know what types the contract expects, we can pass the arguments as `typed values` (ex: BigUIntValue, ListValue, StructValue, etc.) or `bytes`.\n", + "\n", + "We'll first create a transaction for deploying a smart contract using the `controller`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7a579b4c5566ce4d0154a3ab7c65fa534a0b52ca94ee550a149930f59d471c58\n" + ] + } + ], + "source": [ + "from pathlib import Path\n", + "\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "from multiversx_sdk.abi import Abi, BigUIntValue\n", + "\n", + "# prepare the account\n", + "account = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "account.nonce = entrypoint.recall_account_nonce(account.address)\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", + "\n", + "# get the smart contracts controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_smart_contract_controller(abi=abi)\n", + "\n", + "# load the contract bytecode\n", + "bytecode = Path(\"contracts/adder.wasm\").read_bytes()\n", + "\n", + "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", + "args = [BigUIntValue(42)]\n", + "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", + "args = [42]\n", + "\n", + "deploy_transaction = controller.create_transaction_for_deploy(\n", + " sender=account,\n", + " nonce=account.get_nonce_then_increment(),\n", + " bytecode=bytecode,\n", + " gas_limit=5000000,\n", + " arguments=args,\n", + " is_upgradeable=True,\n", + " is_readable=True,\n", + " is_payable=True,\n", + " is_payable_by_sc=True\n", + ")\n", + "\n", + "# broadcasting the transaction\n", + "tx_hash = entrypoint.send_transaction(deploy_transaction)\n", + "print(tx_hash.hex())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When creating transactions using `SmartContractController` or `SmartContractTransactionsFactory`, even if the ABI is available and provided, you can still use _typed value_ objects as arguments for deployments and interactions.\n", + "\n", + "Even further, you can use a mix of typed value objects and plain Python values and objects. For example:\n", + "```py\n", + "args = [U32Value(42), \"hello\", { \"foo\": \"bar\" }, TokenIdentifierValue(\"TEST-123456\")]\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Parsing contract deployment transactions\n", + "\n", + "After broadcasting the transaction, we can wait for it's execution to be completed and parse the processed transaction to extract the address of newly deployed smart contract." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# we use the transaction hash we got when broadcasting the transaction\n", + "contract_deploy_outcome = controller.await_completed_deploy(tx_hash) # waits for transaction completion and parses the result\n", + "contract_address = contract_deploy_outcome.contracts[0].address\n", + "print(contract_address.to_bech32())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we want to wait for transaction completion and parse the result in two different steps, we can do as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# we use the transaction hash we got when broadcasting the transaction\n", + "# waiting for transaction completion\n", + "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "\n", + "# parsing the transaction\n", + "contract_deploy_outcome = controller.parse_deploy(transaction_on_network)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Computing the smart contract address\n", + "\n", + "Even before broadcasting, at the moment you know the sender's address and the nonce for your deployment transaction, you can (deterministically) compute the (upcoming) address of the smart contract:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, AddressComputer\n", + "\n", + "# we used Alice for deploying the contract, so we are using her address\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "\n", + "address_computer = AddressComputer()\n", + "contract_address = address_computer.compute_contract_address(\n", + " deployer=alice,\n", + " deployment_nonce=deploy_transaction.nonce # the same nonce we set on the deploy transaction\n", + ")\n", + "\n", + "print(\"Contract address:\", contract_address.to_bech32())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's create the same transaction using the `factory`. Keep in mind that, after the transaction is created the `nonce` needs to be properly set and the transaction should be signed before broadcasting it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "from multiversx_sdk import Address, DevnetEntrypoint, SmartContractTransactionsOutcomeParser\n", + "from multiversx_sdk.abi import Abi, BigUIntValue\n", + "\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", + "\n", + "# get the smart contracts controller\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_smart_contract_factory(abi=abi)\n", + "\n", + "# load the contract bytecode\n", + "bytecode = Path(\"contracts/adder.wasm\").read_bytes()\n", + "\n", + "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", + "args = [BigUIntValue(42)]\n", + "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", + "args = [42]\n", + "\n", + "alice_address = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "\n", + "deploy_transaction = factory.create_transaction_for_deploy(\n", + " sender=alice_address,\n", + " bytecode=bytecode,\n", + " gas_limit=5000000,\n", + " arguments=args,\n", + " is_upgradeable=True,\n", + " is_readable=True,\n", + " is_payable=True,\n", + " is_payable_by_sc=True\n", + ")\n", + "\n", + "# load the account\n", + "alice = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# set the nonce\n", + "deploy_transaction.nonce = alice.nonce\n", + "\n", + "# sign transaction\n", + "deploy_transaction.signature = alice.sign_transaction(deploy_transaction)\n", + "\n", + "# broadcasting the transaction\n", + "tx_hash = entrypoint.send_transaction(deploy_transaction)\n", + "print(tx_hash.hex())\n", + "\n", + "# waiting for transaction to complete\n", + "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "\n", + "# parsing transaction\n", + "parser = SmartContractTransactionsOutcomeParser(abi)\n", + "contract_deploy_outcome = parser.parse_deploy(transaction_on_network)\n", + "\n", + "contract_address = contract_deploy_outcome.contracts[0].address\n", + "print(contract_address.to_bech32())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Smart Contract calls\n", + "\n", + "In this section we'll see how we can call an endpoint of our previoulsy deployed smart contract using both approaches with the `controller` and the `factory`.\n", + "\n", + "Let's create a smart contract call transaction using the `controller`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "from multiversx_sdk.abi import Abi, BigUIntValue\n", + "\n", + "# prepare the account\n", + "account = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "account.nonce = entrypoint.recall_account_nonce(account.address)\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", + "\n", + "# get the smart contracts controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_smart_contract_controller(abi=abi)\n", + "\n", + "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "\n", + "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", + "args = [BigUIntValue(42)]\n", + "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", + "args = [42]\n", + "\n", + "deploy_transaction = controller.create_transaction_for_execute(\n", + " sender=account,\n", + " nonce=account.get_nonce_then_increment(),\n", + " contract=contract_address,\n", + " gas_limit=5000000,\n", + " function=\"add\",\n", + " arguments=args\n", + ")\n", + "\n", + "# broadcasting the transaction\n", + "tx_hash = entrypoint.send_transaction(deploy_transaction)\n", + "print(tx_hash.hex())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In our case, calling the `add` endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# we use the transaction hash we got when broadcasting the transaction\n", + "contract_call_outcome = controller.await_completed_execute(tx_hash) # waits for transaction completion and parses the result\n", + "values = contract_call_outcome.values" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Aditionally, if our endpoint requires a payment when called, we can also send tokens to the contract when creating a smart contract call transaction. This is supported both on the `controller` and the `factory`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "from multiversx_sdk import Account, DevnetEntrypoint, Token, TokenTransfer\n", + "from multiversx_sdk.abi import Abi, BigUIntValue\n", + "\n", + "# prepare the account\n", + "account = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "account.nonce = entrypoint.recall_account_nonce(account.address)\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", + "\n", + "# get the smart contracts controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_smart_contract_controller(abi=abi)\n", + "\n", + "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "\n", + "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", + "args = [BigUIntValue(42)]\n", + "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", + "args = [42]\n", + "\n", + "# creating the transfer\n", + "first_token = Token(\"TEST-38f249\", 10)\n", + "first_transfer = TokenTransfer(first_token, 1)\n", + "\n", + "second_token = Token(\"BAR-c80d29\")\n", + "second_transfer = TokenTransfer(second_token, 10000000000000000000)\n", + "\n", + "deploy_transaction = controller.create_transaction_for_execute(\n", + " sender=account,\n", + " nonce=account.get_nonce_then_increment(),\n", + " contract=contract_address,\n", + " gas_limit=5000000,\n", + " function=\"add\",\n", + " arguments=args,\n", + " native_transfer_amount=1000000000000000000, # 1 EGLD,\n", + " token_transfers=[first_transfer, second_transfer]\n", + ")\n", + "\n", + "# broadcasting the transaction\n", + "tx_hash = entrypoint.send_transaction(deploy_transaction)\n", + "print(tx_hash.hex())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 024171c7f5ad27f0fc82c87b681eebf0e85758a1 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 3 Dec 2024 15:25:20 +0200 Subject: [PATCH 03/17] wip: finished smart contract examples --- examples/v1.ipynb | 368 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 355 insertions(+), 13 deletions(-) diff --git a/examples/v1.ipynb b/examples/v1.ipynb index da655c78..975d1dd9 100644 --- a/examples/v1.ipynb +++ b/examples/v1.ipynb @@ -694,7 +694,7 @@ "source": [ "## Creating transactions\n", "\n", - "In this section, we'll learn how to create different types of transactions. For creating transactions, we can use `controllers` or `factories`. The `controllers` can be used for scripts or quick network interactions, while the `factories` provide a more granular approach. Usually, the `controllers` use the same parameters as the `factories` but also take an `Account` and the `nonce` of the sender as arguments. The `controllers` also hold some extra functionality, like waiting for transaction completion and parsing transactions. The same functionality can be obtained for `factories` as well, we'll see how in the sections bellow." + "In this section, we'll learn how to create different types of transactions. For creating transactions, we can use `controllers` or `factories`. The `controllers` can be used for scripts or quick network interactions, while the `factories` provide a more granular approach. Usually, the `controllers` use the same parameters as the `factories` but also take an `Account` and the `nonce` of the sender as arguments. The `controllers` also hold some extra functionality, like waiting for transaction completion and parsing transactions. The same functionality can be obtained for transactions built using the `factories` as well, we'll see how in the sections bellow." ] }, { @@ -992,7 +992,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1028,9 +1028,9 @@ "# load the contract bytecode\n", "bytecode = Path(\"contracts/adder.wasm\").read_bytes()\n", "\n", - "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", + "# For deploy arguments, use typed value objects if you haven't provided an ABI\n", "args = [BigUIntValue(42)]\n", - "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", + "# Or use simple, plain Python values and objects if you have provided an ABI\n", "args = [42]\n", "\n", "deploy_transaction = controller.create_transaction_for_deploy(\n", @@ -1137,7 +1137,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, let's create the same transaction using the `factory`. Keep in mind that, after the transaction is created the `nonce` needs to be properly set and the transaction should be signed before broadcasting it." + "Now, let's create the same transaction to deploy a contract using the `factory`. Keep in mind that, after the transaction is created the `nonce` needs to be properly set and the transaction should be signed before broadcasting it." ] }, { @@ -1250,9 +1250,9 @@ "\n", "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", - "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", + "# For deploy arguments, use typed value objects if you haven't provided an ABI\n", "args = [BigUIntValue(42)]\n", - "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", + "# Or use simple, plain Python values and objects if you have provided an ABI\n", "args = [42]\n", "\n", "deploy_transaction = controller.create_transaction_for_execute(\n", @@ -1282,8 +1282,8 @@ "metadata": {}, "outputs": [], "source": [ - "# we use the transaction hash we got when broadcasting the transaction\n", - "contract_call_outcome = controller.await_completed_execute(tx_hash) # waits for transaction completion and parses the result\n", + "# waits for transaction completion and parses the result\n", + "contract_call_outcome = controller.await_completed_execute(tx_hash) # we use the transaction hash we got when broadcasting the transaction\n", "values = contract_call_outcome.values" ] }, @@ -1291,7 +1291,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Aditionally, if our endpoint requires a payment when called, we can also send tokens to the contract when creating a smart contract call transaction. This is supported both on the `controller` and the `factory`." + "Aditionally, if our endpoint requires a payment when called, we can also send tokens to the contract when creating a smart contract call transaction. We can send EGLD, ESDT tokens or both. This is supported both on the `controller` and the `factory`." ] }, { @@ -1323,9 +1323,9 @@ "\n", "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", - "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", + "# For deploy arguments, use typed value objects if you haven't provided an ABI\n", "args = [BigUIntValue(42)]\n", - "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", + "# Or use simple, plain Python values and objects if you have provided an ABI\n", "args = [42]\n", "\n", "# creating the transfer\n", @@ -1335,7 +1335,7 @@ "second_token = Token(\"BAR-c80d29\")\n", "second_transfer = TokenTransfer(second_token, 10000000000000000000)\n", "\n", - "deploy_transaction = controller.create_transaction_for_execute(\n", + "execute_transaction = controller.create_transaction_for_execute(\n", " sender=account,\n", " nonce=account.get_nonce_then_increment(),\n", " contract=contract_address,\n", @@ -1347,9 +1347,351 @@ ")\n", "\n", "# broadcasting the transaction\n", + "tx_hash = entrypoint.send_transaction(execute_transaction)\n", + "print(tx_hash.hex())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's create the same smart contract call transaction, but using the `factory`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "from multiversx_sdk import Account, DevnetEntrypoint, Token, TokenTransfer\n", + "from multiversx_sdk.abi import Abi, BigUIntValue\n", + "\n", + "# prepare the account\n", + "account = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "account.nonce = entrypoint.recall_account_nonce(account.address)\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", + "\n", + "# get the smart contracts controller\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_smart_contract_factory(abi=abi)\n", + "\n", + "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "\n", + "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", + "args = [BigUIntValue(42)]\n", + "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", + "args = [42]\n", + "\n", + "# creating the transfer\n", + "first_token = Token(\"TEST-38f249\", 10)\n", + "first_transfer = TokenTransfer(first_token, 1)\n", + "\n", + "second_token = Token(\"BAR-c80d29\")\n", + "second_transfer = TokenTransfer(second_token, 10000000000000000000)\n", + "\n", + "execute_transaction = factory.create_transaction_for_execute(\n", + " sender=account.address,\n", + " contract=contract_address,\n", + " gas_limit=5000000,\n", + " function=\"add\",\n", + " arguments=args,\n", + " native_transfer_amount=1000000000000000000, # 1 EGLD,\n", + " token_transfers=[first_transfer, second_transfer]\n", + ")\n", + "\n", + "execute_transaction.nonce = account.get_nonce_then_increment()\n", + "execute_transaction.signature = account.sign_transaction(execute_transaction)\n", + "\n", + "# broadcasting the transaction\n", + "tx_hash = entrypoint.send_transaction(execute_transaction)\n", + "print(tx_hash.hex())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Parsing transaction outcome\n", + "\n", + "As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of the transaction, as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint, SmartContractTransactionsOutcomeParser\n", + "from multiversx_sdk.abi import Abi\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", + "\n", + "# create the parser\n", + "parser = SmartContractTransactionsOutcomeParser(abi=abi)\n", + "\n", + "# fetch the transaction of the network\n", + "network_provider = DevnetEntrypoint().network_provider # expose get_transaction in entrypoint\n", + "transaction_on_network = network_provider.get_transaction(tx_hash) # the tx_hash from the transaction sent above\n", + "\n", + "outcome = parser.parse_execute(transaction=transaction_on_network, function=\"add\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Decoding transaction events\n", + "\n", + "You might be interested into decoding events emitted by a contract. You can do so by using the `TransactionEventsParser`.\n", + "\n", + "Suppose we'd like to decode a `startPerformAction` event emitted by the [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract.\n", + "\n", + "First, we load the abi file, then we fetch the transaction, we extract the event from the transaction and then we parse it. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint, TransactionEventsParser, find_events_by_first_topic\n", + "from multiversx_sdk.abi import Abi\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/multisig-full.abi.json\"))\n", + "\n", + "# fetch the transaction of the network\n", + "network_provider = DevnetEntrypoint().network_provider # expose get_transaction in entrypoint\n", + "transaction_on_network = network_provider.get_transaction(tx_hash)\n", + "\n", + "# extract the event from the transaction\n", + "[event] = find_events_by_first_topic(transaction_on_network, \"startPerformAction\")\n", + "\n", + "# create the parser\n", + "events_parser = TransactionEventsParser(abi=abi)\n", + "\n", + "# parse the event\n", + "parsed_event = events_parser.parse_event(event)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Smart Contract queries\n", + "\n", + "When querying a smart contract, a `view function` is called. That function does not modify the state of the contract, thus we don't need to send a transaction.\n", + "\n", + "To query a smart contract, we need to use the `SmartContractController`. Of course, we can use the contract's abi file to encode the arguments of the query, but also parse the result. In this example, we are going to use the [adder](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/adder) smart contract and we'll call the `getSum` endpoint." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint\n", + "from multiversx_sdk.abi import Abi\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", + "\n", + "# the contract address we'll query\n", + "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "\n", + "# create the controller\n", + "sc_controller = DevnetEntrypoint().create_smart_contract_controller(abi=abi)\n", + "\n", + "# creates the query, runs the query, parses the result\n", + "response = sc_controller.query(\n", + " contract=contract_address,\n", + " function=\"getSum\",\n", + " arguments=[] # our function expects no arguments, so we provide an empty list\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we need more granular control, we can split the process in three steps: create the query, run the query and parse the query response. This does the exact same as the example above." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint\n", + "from multiversx_sdk.abi import Abi\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", + "\n", + "# the contract address we'll query\n", + "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "\n", + "# create the controller\n", + "sc_controller = DevnetEntrypoint().create_smart_contract_controller(abi=abi)\n", + "\n", + "# creates the query\n", + "query = sc_controller.create_query(\n", + " contract=contract_address,\n", + " function=\"getSum\",\n", + " arguments=[] # our function expects no arguments, so we provide an empty list\n", + ")\n", + "\n", + "# run the query\n", + "result = sc_controller.run_query(query)\n", + "\n", + "# parse the result\n", + "parsed_result = sc_controller.parse_query_response(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Upgrading a smart contract\n", + "\n", + "Contract upgrade transactions are similar to deployment transactions (see above), in the sense that they also require a contract bytecode. In this context though, the contract address is already known. Similar to deploying a smart contract, we can upgrade a smart contract using either the `controller` or the `factory`.\n", + "\n", + "We'll first upgrade our smart contract using the `controller`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "from multiversx_sdk.abi import Abi, BigUIntValue\n", + "\n", + "# prepare the account\n", + "account = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "account.nonce = entrypoint.recall_account_nonce(account.address)\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", + "\n", + "# get the smart contracts controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_smart_contract_controller(abi=abi)\n", + "\n", + "# load the contract bytecode; this is the new contract code, the one we want to upgrade to\n", + "bytecode = Path(\"contracts/adder.wasm\").read_bytes()\n", + "\n", + "# For deploy arguments, use typed value objects if you haven't provided an ABI\n", + "args = [BigUIntValue(42)]\n", + "# Or use simple, plain Python values and objects if you have provided an ABI\n", + "args = [42]\n", + "\n", + "deploy_transaction = controller.create_transaction_for_upgrade(\n", + " sender=account,\n", + " nonce=account.get_nonce_then_increment(),\n", + " contract=contract_address,\n", + " bytecode=bytecode,\n", + " gas_limit=5000000,\n", + " arguments=args,\n", + " is_upgradeable=True,\n", + " is_readable=True,\n", + " is_payable=True,\n", + " is_payable_by_sc=True\n", + ")\n", + "\n", + "# broadcasting the transaction\n", "tx_hash = entrypoint.send_transaction(deploy_transaction)\n", "print(tx_hash.hex())" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's create the same upgrade transaction using the `factory`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "\n", + "from multiversx_sdk import Address, DevnetEntrypoint, SmartContractTransactionsOutcomeParser\n", + "from multiversx_sdk.abi import Abi, BigUIntValue\n", + "\n", + "\n", + "# load tha abi file\n", + "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", + "\n", + "# get the smart contracts controller\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_smart_contract_factory(abi=abi)\n", + "\n", + "# load the contract bytecode; this is the new contract code, the one we want to upgrade to\n", + "bytecode = Path(\"contracts/adder.wasm\").read_bytes()\n", + "\n", + "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", + "args = [BigUIntValue(42)]\n", + "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", + "args = [42]\n", + "\n", + "alice_address = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "\n", + "deploy_transaction = factory.create_transaction_for_upgrade(\n", + " sender=alice_address,\n", + " bytecode=bytecode,\n", + " gas_limit=5000000,\n", + " arguments=args,\n", + " is_upgradeable=True,\n", + " is_readable=True,\n", + " is_payable=True,\n", + " is_payable_by_sc=True\n", + ")\n", + "\n", + "# load the account\n", + "alice = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# set the nonce\n", + "deploy_transaction.nonce = alice.nonce\n", + "\n", + "# sign transaction\n", + "deploy_transaction.signature = alice.sign_transaction(deploy_transaction)\n", + "\n", + "# broadcasting the transaction\n", + "tx_hash = entrypoint.send_transaction(deploy_transaction)" + ] } ], "metadata": { From b998539fcfce412523c39df69402f77d632767ec Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 6 Dec 2024 18:19:08 +0200 Subject: [PATCH 04/17] wip: update cookbook for accounts --- examples/v1.ipynb | 280 +++++++++++++++++++++++++++--- multiversx_sdk/__init__.py | 4 +- multiversx_sdk/wallet/__init__.py | 3 +- 3 files changed, 257 insertions(+), 30 deletions(-) diff --git a/examples/v1.ipynb b/examples/v1.ipynb index 975d1dd9..9fca75bf 100644 --- a/examples/v1.ipynb +++ b/examples/v1.ipynb @@ -53,11 +53,204 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Creating KeyPairs\n", + "## Creating Accounts\n", "\n", - "...\n", + "We can create an account directly from the entrypoint. Keep in mind that the account you create is network agnostic, it does not matter which entrypoint is used.\n", "\n", - "Maybe create keypair from entrypoint" + "The account can be used for signing and for storing the nonce of the account. It can also be saved to a `pem` or `keystore` file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "account = entrypoint.create_account()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are also other ways to instantiate an `Account`. \n", + "\n", + "#### Instantiating an Account using a secret key" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Account, UserSecretKey\n", + "\n", + "secret_key_hex = \"413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9\"\n", + "secret_key = UserSecretKey(bytes.fromhex(secret_key_hex))\n", + "\n", + "account = Account(secret_key)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Instantiating an Account from a PEM file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account\n", + "\n", + "account = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Instantiating an Account from a Keystore file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account\n", + "\n", + "account = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/alice.json\"),\n", + " password=\"password\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Instantiating an Account from a mnemonic" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Account, Mnemonic\n", + "\n", + "mnemonic = Mnemonic.generate()\n", + "account = Account.new_from_mnemonic(mnemonic.get_text())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Instantiating an Account from a KeyPair" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Account, KeyPair\n", + "\n", + "keypair = KeyPair.generate()\n", + "account = Account.new_from_keypair(keypair)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Managing the Account nonce\n", + "\n", + "The account has a `nonce` property that the user is responsible for keeping up to date. We can fetch the nonce of the account from the network once and then we can increment it with each transaction we create. Each transaction sent **must** have the correct nonce set, otherwise it will not be executed. For more details check out the [Creating Transactions](#creating-transactions) section bellow." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Account, DevnetEntrypoint, UserSecretKey\n", + "\n", + "secret_key_hex = \"413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9\"\n", + "secret_key = UserSecretKey(bytes.fromhex(secret_key_hex))\n", + "\n", + "account = Account(secret_key)\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "account.nonce = entrypoint.recall_account_nonce(account.address)\n", + "\n", + "# When needed, we can get the nonce and increment it\n", + "nonce = account.get_nonce_then_increment()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Saving the Account to a file\n", + "\n", + "We can save the account to either a `pem` file or a `keystore` file. We discourage the use of PEM wallets for storing cryptocurrencies due to their lower security level. However, they prove to be highly convenient and user-friendly for application testing purposes.\n", + "\n", + "#### Saving the Account for a PEM file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, UserSecretKey\n", + "\n", + "secret_key_hex = \"413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9\"\n", + "secret_key = UserSecretKey(bytes.fromhex(secret_key_hex))\n", + "\n", + "account = Account(secret_key)\n", + "account.save_to_pem(path=Path(\"wallet.pem\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Saving the Account to a Keystore file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, UserSecretKey\n", + "\n", + "secret_key_hex = \"413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9\"\n", + "secret_key = UserSecretKey(bytes.fromhex(secret_key_hex))\n", + "\n", + "account = Account(secret_key)\n", + "account.save_to_keystore(path=Path(\"keystoreWallet.json\"), password=\"password\")" ] }, { @@ -66,15 +259,19 @@ "source": [ "## Calling the Faucet\n", "\n", - "..." + "This functionality is not yet available through the entrypoint, but we recommend using the faucet available within the Web Wallet.\n", + "\n", + "- [Testnet Wallet](https://testnet-wallet.multiversx.com/)\n", + "- [Devnet Wallet](https://devnet-wallet.multiversx.com/)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The entrypoint exposes a few methods to directly interact with the network, such as:\n", + "## Interacting with the network\n", "\n", + "The entrypoint exposes a few methods to directly interact with the network, such as:\n", "\n", "- `recall_account_nonce(self, address: Address) -> int;`\n", "- `send_transaction(self, transaction: Transaction) -> bytes;`\n", @@ -118,6 +315,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "## Creating a network provider\n", + "\n", "Additionally, when manually instantiating a network provider, a config can be provided to specify the client name and set custom request options." ] }, @@ -218,7 +417,8 @@ "entrypoint = DevnetEntrypoint()\n", "api = entrypoint.create_network_provider()\n", "\n", - "args = GetBlockArguments(block_hash=bytes.fromhex(\"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"))\n", + "args = GetBlockArguments(block_hash=bytes.fromhex(\n", + " \"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"))\n", "block = api.get_block(arguments=args)" ] }, @@ -240,7 +440,8 @@ "entrypoint = DevnetEntrypoint()\n", "api = entrypoint.create_network_provider()\n", "\n", - "args = GetBlockArguments(block_hash=bytes.fromhex(\"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"))\n", + "args = GetBlockArguments(block_hash=bytes.fromhex(\n", + " \"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"))\n", "latest_block = api.get_latest_block(shard=1)" ] }, @@ -338,9 +539,11 @@ "entrypoint = DevnetEntrypoint()\n", "api = entrypoint.create_network_provider()\n", "\n", + "\n", "def condition_to_be_satisfied(account: AccountOnNetwork) -> bool:\n", " return account.balance >= 7000000000000000000 # 7 EGLD\n", "\n", + "\n", "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", "account = api.await_account_on_condition(address=alice, condition=condition_to_be_satisfied)" ] @@ -493,12 +696,15 @@ "entrypoint = DevnetEntrypoint()\n", "api = entrypoint.create_network_provider()\n", "\n", - "# can be the creation of an event or something else\n", + "\n", "def condition_to_be_satisfied(transaction_on_network: TransactionOnNetwork) -> bool:\n", + " # can be the creation of an event or something else\n", " ...\n", "\n", + "\n", "tx_hash = \"exampletransactionhash\"\n", - "transaction_on_network = api.await_transaction_on_condition(transaction_hash=tx_hash, condition=condition_to_be_satisfied)" + "transaction_on_network = api.await_transaction_on_condition(\n", + " transaction_hash=tx_hash, condition=condition_to_be_satisfied)" ] }, { @@ -626,7 +832,8 @@ "fungible_token_definition = api.get_definition_of_fungible_token(token_identifier=\"TEST-123456\")\n", "\n", "# used for MetaESDT, SFT, NFT\n", - "non_fungible_token_definition = api.get_definition_of_tokens_collection(collection_name=\"NFT-987654\")" + "non_fungible_token_definition = api.get_definition_of_tokens_collection(\n", + " collection_name=\"NFT-987654\")" ] }, { @@ -718,7 +925,7 @@ "\n", "# getting the controller and the factory from the entrypoint\n", "transfers_controller = entrypoint.create_transfers_controller()\n", - "transfers_factory = entrypoint.create_transfers_factory()\n", + "transfers_factory = entrypoint.create_transfers_transactions_factory()\n", "\n", "# manually instantiating the controller and the factory\n", "controller = TransfersController(chain_id=\"D\")\n", @@ -768,7 +975,8 @@ "transaction = transfers_controller.create_transaction_for_transfer(\n", " sender=account,\n", " nonce=account.get_nonce_then_increment(),\n", - " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " receiver=Address.new_from_bech32(\n", + " \"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " native_transfer_amount=1000000000000000000, # 1 EGLD\n", ")" ] @@ -839,7 +1047,8 @@ "transaction = transfers_controller.create_transaction_for_transfer(\n", " sender=account,\n", " nonce=account.get_nonce_then_increment(),\n", - " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " receiver=Address.new_from_bech32(\n", + " \"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " token_transfers=[first_transfer, second_transfer]\n", ")" ] @@ -914,7 +1123,8 @@ "transaction = transfers_controller.create_transaction_for_transfer(\n", " sender=account,\n", " nonce=account.get_nonce_then_increment(),\n", - " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " receiver=Address.new_from_bech32(\n", + " \"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " native_transfer_amount=1000000000000000000, # 1 EGLD\n", " token_transfers=[first_transfer, second_transfer]\n", ")" @@ -1157,7 +1367,7 @@ "\n", "# get the smart contracts controller\n", "entrypoint = DevnetEntrypoint()\n", - "factory = entrypoint.create_smart_contract_factory(abi=abi)\n", + "factory = entrypoint.create_smart_contract_transactions_factory(abi=abi)\n", "\n", "# load the contract bytecode\n", "bytecode = Path(\"contracts/adder.wasm\").read_bytes()\n", @@ -1167,7 +1377,8 @@ "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", "args = [42]\n", "\n", - "alice_address = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "alice_address = Address.new_from_bech32(\n", + " \"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", "\n", "deploy_transaction = factory.create_transaction_for_deploy(\n", " sender=alice_address,\n", @@ -1248,7 +1459,8 @@ "entrypoint = DevnetEntrypoint()\n", "controller = entrypoint.create_smart_contract_controller(abi=abi)\n", "\n", - "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "contract_address = Address.new_from_bech32(\n", + " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "# For deploy arguments, use typed value objects if you haven't provided an ABI\n", "args = [BigUIntValue(42)]\n", @@ -1283,7 +1495,8 @@ "outputs": [], "source": [ "# waits for transaction completion and parses the result\n", - "contract_call_outcome = controller.await_completed_execute(tx_hash) # we use the transaction hash we got when broadcasting the transaction\n", + "# we use the transaction hash we got when broadcasting the transaction\n", + "contract_call_outcome = controller.await_completed_execute(tx_hash)\n", "values = contract_call_outcome.values" ] }, @@ -1321,7 +1534,8 @@ "entrypoint = DevnetEntrypoint()\n", "controller = entrypoint.create_smart_contract_controller(abi=abi)\n", "\n", - "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "contract_address = Address.new_from_bech32(\n", + " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "# For deploy arguments, use typed value objects if you haven't provided an ABI\n", "args = [BigUIntValue(42)]\n", @@ -1383,9 +1597,10 @@ "\n", "# get the smart contracts controller\n", "entrypoint = DevnetEntrypoint()\n", - "factory = entrypoint.create_smart_contract_factory(abi=abi)\n", + "factory = entrypoint.create_smart_contract_transactions_factory(abi=abi)\n", "\n", - "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "contract_address = Address.new_from_bech32(\n", + " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", "args = [BigUIntValue(42)]\n", @@ -1443,7 +1658,8 @@ "\n", "# fetch the transaction of the network\n", "network_provider = DevnetEntrypoint().network_provider # expose get_transaction in entrypoint\n", - "transaction_on_network = network_provider.get_transaction(tx_hash) # the tx_hash from the transaction sent above\n", + "transaction_on_network = network_provider.get_transaction(\n", + " tx_hash) # the tx_hash from the transaction sent above\n", "\n", "outcome = parser.parse_execute(transaction=transaction_on_network, function=\"add\")" ] @@ -1511,7 +1727,8 @@ "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# the contract address we'll query\n", - "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "contract_address = Address.new_from_bech32(\n", + " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "# create the controller\n", "sc_controller = DevnetEntrypoint().create_smart_contract_controller(abi=abi)\n", @@ -1544,7 +1761,8 @@ "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# the contract address we'll query\n", - "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "contract_address = Address.new_from_bech32(\n", + " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "# create the controller\n", "sc_controller = DevnetEntrypoint().create_smart_contract_controller(abi=abi)\n", @@ -1609,6 +1827,9 @@ "# Or use simple, plain Python values and objects if you have provided an ABI\n", "args = [42]\n", "\n", + "contract_address = Address.new_from_bech32(\n", + " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "\n", "deploy_transaction = controller.create_transaction_for_upgrade(\n", " sender=account,\n", " nonce=account.get_nonce_then_increment(),\n", @@ -1642,7 +1863,7 @@ "source": [ "from pathlib import Path\n", "\n", - "from multiversx_sdk import Address, DevnetEntrypoint, SmartContractTransactionsOutcomeParser\n", + "from multiversx_sdk import Address, DevnetEntrypoint\n", "from multiversx_sdk.abi import Abi, BigUIntValue\n", "\n", "\n", @@ -1651,7 +1872,7 @@ "\n", "# get the smart contracts controller\n", "entrypoint = DevnetEntrypoint()\n", - "factory = entrypoint.create_smart_contract_factory(abi=abi)\n", + "factory = entrypoint.create_smart_contract_transactions_factory(abi=abi)\n", "\n", "# load the contract bytecode; this is the new contract code, the one we want to upgrade to\n", "bytecode = Path(\"contracts/adder.wasm\").read_bytes()\n", @@ -1661,10 +1882,15 @@ "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", "args = [42]\n", "\n", - "alice_address = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "alice_address = Address.new_from_bech32(\n", + " \"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "\n", + "contract_address = Address.new_from_bech32(\n", + " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "deploy_transaction = factory.create_transaction_for_upgrade(\n", " sender=alice_address,\n", + " contract=contract_address,\n", " bytecode=bytecode,\n", " gas_limit=5000000,\n", " arguments=args,\n", diff --git a/multiversx_sdk/__init__.py b/multiversx_sdk/__init__.py index a54f9b06..59539540 100644 --- a/multiversx_sdk/__init__.py +++ b/multiversx_sdk/__init__.py @@ -53,7 +53,7 @@ UnPauseOutcome, UpdateAttributesOutcome, WipeOutcome) from multiversx_sdk.transfers import (TransfersController, TransferTransactionsFactory) -from multiversx_sdk.wallet import (Mnemonic, UserPEM, UserPublicKey, +from multiversx_sdk.wallet import (KeyPair, Mnemonic, UserPEM, UserPublicKey, UserSecretKey, UserSigner, UserVerifier, UserWallet, ValidatorPEM, ValidatorPublicKey, ValidatorSecretKey, @@ -82,5 +82,5 @@ "AccountOnNetwork", "AccountStorage", "AccountStorageEntry", "AwaitingOptions", "BlockCoordinates", "BlockOnNetwork", "FungibleTokenMetadata", "GetBlockArguments", "NetworkConfig", "NetworkStatus", "TokenAmountOnNetwork", "TokensCollectionMetadata", "TransactionCostResponse", "AccountAwaiter", - "LibraryConfig" + "LibraryConfig", "KeyPair" ] diff --git a/multiversx_sdk/wallet/__init__.py b/multiversx_sdk/wallet/__init__.py index 140dae5f..dd83d268 100644 --- a/multiversx_sdk/wallet/__init__.py +++ b/multiversx_sdk/wallet/__init__.py @@ -1,3 +1,4 @@ +from multiversx_sdk.wallet.keypair import KeyPair from multiversx_sdk.wallet.mnemonic import Mnemonic from multiversx_sdk.wallet.user_keys import UserPublicKey, UserSecretKey from multiversx_sdk.wallet.user_pem import UserPEM @@ -15,5 +16,5 @@ "UserPublicKey", "ValidatorSecretKey", "ValidatorPublicKey", "UserVerifier", "ValidatorSigner", "ValidatorVerifier", "ValidatorPEM", - "UserWallet", "UserPEM" + "UserWallet", "UserPEM", "KeyPair" ] From 06d8184b5a2ebf169b857bfb278d9b06019f18ff Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 9 Dec 2024 17:09:23 +0200 Subject: [PATCH 05/17] wip: cookbook update --- examples/v1.ipynb | 1077 ++++++++++++++++- .../delegation/delegation_controller.py | 2 +- 2 files changed, 1023 insertions(+), 56 deletions(-) diff --git a/examples/v1.ipynb b/examples/v1.ipynb index 9fca75bf..48b9ba27 100644 --- a/examples/v1.ipynb +++ b/examples/v1.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -17,7 +17,7 @@ "source": [ "## Creating an Entrypoint\n", "\n", - "The Entrypoint represents a network client that makes the most common operations easily accessible. We have an entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint` and `LocalnetEntrypoint`. We are going to interact with the Devnet so we create an entrypoint as follows:" + "The Entrypoint represents a network client that makes the most common operations easily accessible. We have an entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint` and `LocalnetEntrypoint`. We are going to interact with the Devnet, so we create an entrypoint as follows:" ] }, { @@ -49,6 +49,24 @@ "entrypoint = DevnetEntrypoint(url=\"https://custom-multiversx-devnet-api.com\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By default, an Entrypoint, in our case the `DevnetEntrypoint`, uses the api, but we can also create a custom one that interacts with the proxy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint\n", + "\n", + "custom_entrypoint = DevnetEntrypoint(url=\"https:devnet-gateway.multiversx.com\", kind=\"proxy\")" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -276,20 +294,9 @@ "- `recall_account_nonce(self, address: Address) -> int;`\n", "- `send_transaction(self, transaction: Transaction) -> bytes;`\n", "- `send_transactions(self, transactions: list[Transaction]) -> tuple[int, list[bytes]];`\n", - "- `await_completed_transaction(self, tx_hash: Union[str, bytes]) -> TransactionOnNetwork;`\n", - "\n", - "Some other methods are exposed through a so called network provider. There are two types of network providers: ApiNetworkProvider and ProxyNetworkProvider. The ProxyNetworkProvider interacts directly with the proxy of an observing squad. The ApiNetworkProvider, as the name suggests, interacts with the api, that is a layer over the proxy. It fetches data from the network but also from the Elastic Search. By default, an Entrypoint, in our case the `DevnetEntrypoint`, uses the api, but we can also create a custom one that interacts with the proxy." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from multiversx_sdk import DevnetEntrypoint\n", + "- `await_completed_transaction(self, tx_hash: str | bytes) -> TransactionOnNetwork;`\n", "\n", - "custom_entrypoint = DevnetEntrypoint(url=\"https:devnet-gateway.multiversx.com\", kind=\"proxy\")" + "Some other methods are exposed through a so called network provider. There are two types of network providers: ApiNetworkProvider and ProxyNetworkProvider. The ProxyNetworkProvider interacts directly with the proxy of an observing squad. The ApiNetworkProvider, as the name suggests, interacts with the api, that is a layer over the proxy. It fetches data from the network but also from Elastic Search." ] }, { @@ -554,7 +561,7 @@ "source": [ "### Sending and simulating transactions\n", "\n", - "In order for our transactions to be executed, we use the network providers to broadcast them to the network. We'll discuss more about transactions in the dedicated section, but for now, we'll see minimal example. Keep in mind that, in order for transactions to be processed they need to be signed.\n", + "In order for our transactions to be executed, we use the network providers to broadcast them to the network. We'll discuss more about transactions in the dedicated section, but for now, we'll see a minimal example. Keep in mind that, in order for transactions to be processed they need to be signed.\n", "\n", "#### Sending a transaction" ] @@ -573,7 +580,7 @@ "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", "\n", - "# this transaction is not signed; if this code is executed it will raise an error\n", + "# this transaction is not signed\n", "transaction = Transaction(\n", " sender=alice,\n", " receiver=bob,\n", @@ -581,6 +588,7 @@ " chain_id=\"D\"\n", ")\n", "\n", + "# the API will return an error because the transaction is not signed\n", "transaction_hash = api.send_transaction(transaction)" ] }, @@ -857,7 +865,7 @@ "api = entrypoint.create_network_provider()\n", "\n", "query = SmartContractQuery(\n", - " contract=Address.new_from_bech32(\"\"),\n", + " contract=Address.new_from_bech32(\"erd1qqqqq...\"),\n", " function=\"testFunction\",\n", " arguments=[]\n", ")\n", @@ -947,9 +955,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Native token transfers\n", + "#### Native token transfers using the controller\n", "\n", - "First, we'll create the transaction using the controller. Because we'll use an `Account`, the transaction we'll be signed." + "Because we'll use an `Account`, the transaction we'll be signed." ] }, { @@ -978,14 +986,18 @@ " receiver=Address.new_from_bech32(\n", " \"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " native_transfer_amount=1000000000000000000, # 1 EGLD\n", - ")" + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now, let's create the same transaction using the `factory`. Because we only use the address of the sender, the transactions are not going to be signed or have the nonce field set properly. This should be taken care after the transaction is created." + "#### Native token transfers using the factory\n", + "\n", + "Because we only use the address of the sender, the transactions are not going to be signed or have the nonce field set properly. This should be taken care after the transaction is created." ] }, { @@ -994,28 +1006,40 @@ "metadata": {}, "outputs": [], "source": [ - "from multiversx_sdk import TransferTransactionsFactory, TransactionsFactoryConfig\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", "\n", - "config = TransactionsFactoryConfig(chain_id=\"D\")\n", - "factory = TransferTransactionsFactory(config=config)\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_transfers_transactions_factory()\n", + "\n", + "alice = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", "\n", "transaction = factory.create_transaction_for_transfer(\n", - " sender=alice,\n", + " sender=alice.address,\n", " receiver=bob,\n", " native_amount=1000000000000000000 # 1 EGLD\n", - ")" + ")\n", + "# set the sender's nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# sign the transaction using the sender's account\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Custom token transfers\n", - "\n", - "We'll use the same `controller` and `factory` to send custom tokens." + "#### Custom token transfers using the controller" ] }, { @@ -1029,13 +1053,13 @@ "\n", "entrypoint = DevnetEntrypoint()\n", "\n", - "account = Account.new_from_keystore(\n", + "alice = Account.new_from_keystore(\n", " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", " password=\"password\",\n", " address_index=0\n", ")\n", "# the user is responsible for managing the nonce\n", - "account.nonce = entrypoint.recall_account_nonce(account.address)\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", "esdt = Token(identifier=\"TEST-123456\")\n", "first_transfer = TokenTransfer(token=esdt, amount=1000000000)\n", @@ -1045,8 +1069,8 @@ "\n", "transfers_controller = entrypoint.create_transfers_controller()\n", "transaction = transfers_controller.create_transaction_for_transfer(\n", - " sender=account,\n", - " nonce=account.get_nonce_then_increment(),\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", " receiver=Address.new_from_bech32(\n", " \"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " token_transfers=[first_transfer, second_transfer]\n", @@ -1057,7 +1081,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, let's create the same transaction using the `factory`. Because we only use the address of the sender, the transactions are not going to be signed or have the nonce field set properly. This should be taken care after the transaction is created." + "#### Custom token transafers using the factory\n", + "\n", + "Because we only use the address of the sender, the transactions are not going to be signed or have the nonce field set properly. This should be taken care after the transaction is created." ] }, { @@ -1066,12 +1092,18 @@ "metadata": {}, "outputs": [], "source": [ - "from multiversx_sdk import TransferTransactionsFactory, TransactionsFactoryConfig, Token, TokenTransfer\n", + "from multiversx_sdk import Account, DevnetEntrypoint, Token, TokenTransfer\n", "\n", - "config = TransactionsFactoryConfig(chain_id=\"D\")\n", - "factory = TransferTransactionsFactory(config=config)\n", + "entrypoint = DevnetEntrypoint()\n", + "\n", + "alice = Account.new_from_keystore(\n", + " file_path=Path(\"../multiversx_sdk/testutils/testwallets/withDummyMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=0\n", + ")\n", + "# the user is responsible for managing the nonce\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", "\n", "esdt = Token(identifier=\"TEST-123456\")\n", @@ -1081,10 +1113,18 @@ "second_transfer = TokenTransfer(token=nft, amount=1)\n", "\n", "transaction = factory.create_transaction_for_transfer(\n", - " sender=alice,\n", + " sender=alice.address,\n", " receiver=bob,\n", " token_transfers=[first_transfer, second_transfer]\n", - ")" + ")\n", + "\n", + "# set the sender's nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# sign the transaction using the sender's account\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" ] }, { @@ -1127,7 +1167,9 @@ " \"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " native_transfer_amount=1000000000000000000, # 1 EGLD\n", " token_transfers=[first_transfer, second_transfer]\n", - ")" + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" ] }, { @@ -1204,15 +1246,7 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "7a579b4c5566ce4d0154a3ab7c65fa534a0b52ca94ee550a149930f59d471c58\n" - ] - } - ], + "outputs": [], "source": [ "from pathlib import Path\n", "\n", @@ -1800,7 +1834,7 @@ "source": [ "from pathlib import Path\n", "\n", - "from multiversx_sdk import Account, DevnetEntrypoint\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", "from multiversx_sdk.abi import Abi, BigUIntValue\n", "\n", "# prepare the account\n", @@ -1863,7 +1897,7 @@ "source": [ "from pathlib import Path\n", "\n", - "from multiversx_sdk import Address, DevnetEntrypoint\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", "from multiversx_sdk.abi import Abi, BigUIntValue\n", "\n", "\n", @@ -1918,6 +1952,939 @@ "# broadcasting the transaction\n", "tx_hash = entrypoint.send_transaction(deploy_transaction)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Token management\n", + "\n", + "In this section, we're going to create transactions to issue fungible tokens, issue semi-fungible tokens, create NFTs, set token roles, but also parse these transactions to extract their outcome (e.g. get the token indentifier of the newly issued token).\n", + "\n", + "Of course, the methods used here are available through the `TokenManagementController` or through the `TokenManagementTransactionsFactory`. The controller also contains methods for awaiting transaction completion and for parsing the transaction outcome. The same can be achieved for the transactions factory by using the `TokenManagementTransactionsOutcomeParser`. For scripts or quick network interactions we advise you use the controller, but for a more granular approach (e.g. DApps) we suggest using the factory.\n", + "\n", + "#### Issuing fungible tokens" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_token_management_controller()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "transaction = controller.create_transaction_for_issuing_fungible(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", + " num_decimals=6,\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True\n", + ")\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# wait for transaction to execute, extract the token identifier\n", + "outcome = controller.await_completed_issue_fungible(tx_hash)\n", + "\n", + "token_identifier = outcome[0].token_identifier" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Setting special roles for fungible tokens" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_token_management_controller()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "transaction = controller.create_transaction_for_setting_special_role_on_fungible_token(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " user=bob,\n", + " token_identifier=\"TEST-123456\",\n", + " add_role_local_mint=True,\n", + " add_role_local_burn=True,\n", + " add_role_esdt_transfer_role=True\n", + ")\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# wait for transaction to execute, extract the roles\n", + "outcome = controller.await_completed_set_special_role_on_fungible_token(tx_hash)\n", + "\n", + "roles = outcome[0].roles\n", + "uaser = outcome[0].user_address" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Issuing semi-fungible tokens" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_token_management_controller()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "transaction = controller.create_transaction_for_issuing_semi_fungible(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_transfer_nft_create_role=True,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True\n", + ")\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# wait for transaction to execute, extract the token identifier\n", + "outcome = controller.await_completed_issue_semi_fungible(tx_hash)\n", + "\n", + "token_identifier = outcome[0].token_identifier" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Issuing NFT collection & creating NFTs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_token_management_controller()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "transaction = controller.create_transaction_for_issuing_non_fungible(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_transfer_nft_create_role=True,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True\n", + ")\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# wait for transaction to execute, extract the collection identifier\n", + "outcome = controller.await_completed_issue_non_fungible(tx_hash)\n", + "\n", + "collection_identifier = outcome[0].token_identifier\n", + "\n", + "# create a NFT\n", + "transaction = controller.create_transaction_for_creating_nft(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " token_identifier=collection_identifier,\n", + " initial_quantity=1,\n", + " name=\"TEST\",\n", + " royalties=2500, # 25%\n", + " hash=\"\",\n", + " attributes=b\"\",\n", + " uris=[]\n", + ")\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# wait for transaction to execute, extract the nft identifier\n", + "outcome = controller.await_completed_create_nft(tx_hash)\n", + "\n", + "identifier = outcome[0].token_identifier\n", + "nonce = outcome[0].nonce\n", + "initial_quantity = outcome[0].initial_quantity" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "These are just a few examples of what we can do using the token management controller or factory. For a full list of what methods are supported for both, check out the autogenerated documentation:\n", + "- [TokenManagementController](#)\n", + "- [TokenManagementTransactionsFactory](#)\n", + "\n", + "### Account management\n", + "\n", + "The account management controller and factory allow us to create transactions for managing accounts, like guarding and unguarding accounts and saving key-value pairs.\n", + "\n", + "#### Guarding an account\n", + "\n", + "To read more about Guardians, check out the [documentation](https://docs.multiversx.com/developers/built-in-functions/#setguardian).\n", + "\n", + "A guardian can also be set using the WebWallet. The wallet uses our hosted `Trusted Co-Signer Service`. Check out the steps to guard an account using the wallet [here](https://docs.multiversx.com/wallet/web-wallet/#guardian).\n", + "\n", + "Keep in mind that, all the methods presented bellow are available in the `AccountManagementTransactionsFactory`, as well." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_account_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# we can use a trusted service that provides a guardian, or simply set another address we own or trust\n", + "guardian = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "transaction = controller.create_transaction_for_setting_guardian(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " guardian_address=guardian,\n", + " service_id=\"SelfOwnedAddress\" # this is just an example\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After we've set a guardian, we have to wait 20 epochs until we can activate the guardian. After the guardian is set, all the transactions we send should be signed by the guardian, as well. To activate the guardian we can do as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_account_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "transaction = controller.create_transaction_for_guarding_account(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment()\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If at some point, we want to disable our guardian, we can send the following transaction:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_account_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "guardian = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "transaction = controller.create_transaction_for_unguarding_account(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " guardian=guardian\n", + ")\n", + "\n", + "# the transaction should also be signed by the guardian before being sent\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Saving a key-value pair to an account\n", + "\n", + "We can store key-value pairs for an acoount on the network. To do so, we create the following transaction:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_account_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# creating the key-value pairs we want to save\n", + "values = {\n", + " \"testKey\".encode(): \"testValue\".encode(),\n", + " b\"anotherKey\": b\"anotherValue\"\n", + "}\n", + "\n", + "transaction = controller.create_transaction_for_saving_key_value(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " key_value_pairs=values\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Delegation management\n", + "\n", + "To read more about staking providers and delegation, please check out the [docs](https://docs.multiversx.com/validators/delegation-manager/#introducing-staking-providers). \n", + "\n", + "In this section, we are going to create a new delegation contract, get the address of the contract, delegate funds to the contract, redelegate rewards, claim rewards, undelegate and withdraw funds from the contract. The operations can be performed using both the `controller` and the `factory`. For a full list of all the methods supported check out the auto-generated documentation:\n", + "- [DelegationController](#)\n", + "- [DelegationTransactionsFactory](#)\n", + "\n", + "#### Creating a new delegation contract" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_delegation_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "transaction = controller.create_transaction_for_new_delegation_contract(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " total_delegation_cap=0, # uncapped,\n", + " service_fee=0,\n", + " amount=1250000000000000000000 # 1250 EGLD\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# wait for transaction completion, extract delegation contract's address\n", + "outcome = controller.await_completed_create_new_delegation_contract(tx_hash)\n", + "\n", + "contract_address = outcome[0].contract_address" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Delegating funds to the contract\n", + "\n", + "We can send funds to a delegation contract to earn rewards." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_delegation_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# delegation contract\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "\n", + "transaction = controller.create_transaction_for_delegating(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " delegation_contract=contract,\n", + " amount=5000000000000000000000 # 5000 EGLD\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Redelegating rewards\n", + "\n", + "After a period of time, we might have enough rewards that we want to redelegate to the contract to earn even more rewards. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_delegation_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# delegation contract\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "\n", + "transaction = controller.create_transaction_for_redelegating_rewards(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " delegation_contract=contract\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Claiming rewards\n", + "\n", + "We can also claim our rewards." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_delegation_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# delegation contract\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "\n", + "transaction = controller.create_transaction_for_claiming_rewards(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " delegation_contract=contract\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Undelegating funds\n", + "\n", + "By undelegating we let the contract know we want to get back our staked funds. This operation has a 10 epochs unbonding period." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_delegation_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# delegation contract\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "\n", + "transaction = controller.create_transaction_for_undelegating(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " delegation_contract=contract,\n", + " amount=1000000000000000000000 # 1000 EGLD\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Withdrawing funds\n", + "\n", + "After the unbonding period has passed, we can withdraw our funds from the contract" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_delegation_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# delegation contract\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "\n", + "transaction = controller.create_transaction_for_withdrawing(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " delegation_contract=contract\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Relayed transactions\n", + "\n", + "We are currently on the third iteration of relayed transactions. V1 and V2 are soon to be deactivated so we'll focus on V3. For V3, two new fields have been added on transactions: `releyer` and `relayerSignature`. Before the sender signs the transaction, the relayer needs to be set. After the sender has signed the transaction, the relayer can also sign the transaction and broadcast it. Keep in mind that, for relayed V3 transactions we need an extra `50_000` gas. Let's see how we can create a relayed transaction:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint, Transaction\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "# carol will be our relayer, that means she is paying the gas for the transaction\n", + "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "\n", + "# fetch the sender's nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# create the transaction\n", + "transaction = Transaction(\n", + " sender=alice.address,\n", + " receiver=bob,\n", + " gas_limit=110_000,\n", + " chain_id=\"D\",\n", + " nonce=alice.get_nonce_then_increment(),\n", + " relayer=carol.address,\n", + " data=\"hello\".encode()\n", + ")\n", + "\n", + "# sender signs the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# relayer signs the transaction\n", + "transaction.relayer_signature = carol.sign_transaction(transaction)\n", + "\n", + "# broadcast the transcation\n", + "entrypoint = DevnetEntrypoint()\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Creating relayed transactions using controllers\n", + "\n", + "We can create relayed transactions using any of the controllers. Each controller has a `relayer` argument, that can be set if we want to create a relayed transaction. Let's issue a fungible token creating a relayed transaction:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_token_management_controller()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# carol will be our relayer, that means she is paying the gas for the transaction\n", + "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "transaction = controller.create_transaction_for_issuing_fungible(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", + " num_decimals=6,\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True,\n", + " relayer=carol.address\n", + ")\n", + "\n", + "# relayer also signs the transaction\n", + "transaction.relayer_signature = carol.sign_transaction(transaction)\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Create relayed transactions using factories\n", + "\n", + "The transactions factories do not have a `relayer` argument, the relayer needs to be set after creating the transaction. This is good because the transaction is not signed by the sender when created. Let's issue a fungible token using the `TokenManagementTransactionsFactory`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management factory\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_token_management_transactions_factory()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# carol will be our relayer, that means she is paying the gas for the transaction\n", + "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "\n", + "transaction = factory.create_transaction_for_issuing_fungible(\n", + " sender=alice.address,\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", + " num_decimals=6,\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True\n", + ")\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# set the nonce of the sender\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# set the relayer\n", + "transaction.relayer = carol.address\n", + "\n", + "# sender signs the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# relayer signs the transaction\n", + "transaction.relayer_signature = carol.sign_transaction(transaction)\n", + "\n", + "# broadcast the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Creating guarded transactions using controllers\n", + "\n", + "Very similar to relayers, we have a field `guardian` and a field `guardianSignature`. Each controller has an argument for the guardian. The transaction can be sent to a service that signs it using the guardian's account or we can use another account as a guardian. Let's issue a token using a guarded account." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_token_management_controller()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# carol is the guardian\n", + "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "transaction = controller.create_transaction_for_issuing_fungible(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", + " num_decimals=6,\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True,\n", + " guardian=carol.address\n", + ")\n", + "\n", + "# guardian also signs the transaction\n", + "transaction.guardian_signature = carol.sign_transaction(transaction)\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Creating guarded transactions using factories\n", + "\n", + "The transactions factories do not have a `guardian` argument, the guardian needs to be set after creating the transaction. This is good because the transaction is not signed by the sender when created. Let's issue a fungible token using the `TokenManagementTransactionsFactory`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management factory\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_token_management_transactions_factory()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# carol is the guardian\n", + "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "\n", + "transaction = factory.create_transaction_for_issuing_fungible(\n", + " sender=alice.address,\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", + " num_decimals=6,\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True\n", + ")\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# set the nonce of the sender\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# set the guardian\n", + "transaction.guardian = carol.address\n", + "\n", + "# sender signs the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# guardian signs the transaction\n", + "transaction.guardian_signature = carol.sign_transaction(transaction)\n", + "\n", + "# broadcast the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also create guarded relayed transactions the same way we did before. Keep in mind that, only the sender can be guarded, the relayer cannot. The same flow can be used. Using controllers, we set both `guardian` and `relayer` fields and then the transaction should be signed by both. Using a factory, we create the transaction, set both both fields and then sign the transaction using the sender's account, then the the guardian and the relayer sign the transaction." + ] } ], "metadata": { @@ -1936,7 +2903,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.13.0" } }, "nbformat": 4, diff --git a/multiversx_sdk/delegation/delegation_controller.py b/multiversx_sdk/delegation/delegation_controller.py index fc3f9fe6..8520a16c 100644 --- a/multiversx_sdk/delegation/delegation_controller.py +++ b/multiversx_sdk/delegation/delegation_controller.py @@ -52,7 +52,7 @@ def parse_create_new_delegation_contract(self, return self.parser.parse_create_new_delegation_contract(transaction_on_network) def await_completed_create_new_delegation_contract(self, - transaction_hash: str) -> list[CreateNewDelegationContractOutcome]: + transaction_hash: Union[str, bytes]) -> list[CreateNewDelegationContractOutcome]: transaction = self.network_provider.await_transaction_completed(transaction_hash) return self.parse_create_new_delegation_contract(transaction) From d3c752d515495af048da4adbde5fe0677c4db55f Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 10 Dec 2024 17:13:24 +0200 Subject: [PATCH 06/17] wip: mostly finished --- examples/v1.ipynb | 2121 ++++++++++++++--- .../account_management/account_controller.py | 6 +- .../account_transactions_factory.py | 3 +- multiversx_sdk/entrypoints/entrypoints.py | 3 + 4 files changed, 1807 insertions(+), 326 deletions(-) diff --git a/examples/v1.ipynb b/examples/v1.ipynb index 48b9ba27..94714fbd 100644 --- a/examples/v1.ipynb +++ b/examples/v1.ipynb @@ -291,10 +291,11 @@ "\n", "The entrypoint exposes a few methods to directly interact with the network, such as:\n", "\n", - "- `recall_account_nonce(self, address: Address) -> int;`\n", - "- `send_transaction(self, transaction: Transaction) -> bytes;`\n", - "- `send_transactions(self, transactions: list[Transaction]) -> tuple[int, list[bytes]];`\n", - "- `await_completed_transaction(self, tx_hash: str | bytes) -> TransactionOnNetwork;`\n", + "- `recall_account_nonce(address: Address) -> int;`\n", + "- `send_transaction(transaction: Transaction) -> bytes;`\n", + "- `send_transactions(transactions: list[Transaction]) -> tuple[int, list[bytes]];`\n", + "- `get_transaction(tx_hash: str | bytes) -> TransactionOnNetwork;`\n", + "- `await_completed_transaction(tx_hash: str | bytes) -> TransactionOnNetwork;`\n", "\n", "Some other methods are exposed through a so called network provider. There are two types of network providers: ApiNetworkProvider and ProxyNetworkProvider. The ProxyNetworkProvider interacts directly with the proxy of an observing squad. The ApiNetworkProvider, as the name suggests, interacts with the api, that is a layer over the proxy. It fetches data from the network but also from Elastic Search." ] @@ -592,6 +593,57 @@ "transaction_hash = api.send_transaction(transaction)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sending multiple transactions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, DevnetEntrypoint, Transaction\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "api = entrypoint.create_network_provider()\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "# this transaction is not signed\n", + "first_transaction = Transaction(\n", + " sender=alice,\n", + " receiver=bob,\n", + " gas_limit=50000,\n", + " chain_id=\"D\",\n", + " nonce=2\n", + ")\n", + "\n", + "second_transaction = Transaction(\n", + " sender=bob,\n", + " receiver=alice,\n", + " gas_limit=50000,\n", + " chain_id=\"D\",\n", + " nonce=1\n", + ")\n", + "\n", + "third_transaction = Transaction(\n", + " sender=alice,\n", + " receiver=alice,\n", + " gas_limit=60000,\n", + " chain_id=\"D\",\n", + " nonce=3,\n", + " data=b\"hello\"\n", + ")\n", + "\n", + "# the API will return an error because the transactions are not signed\n", + "num_of_txs, hashes = api.send_transactions([first_transaction, second_transaction, third_transaction])" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -711,8 +763,7 @@ "\n", "\n", "tx_hash = \"exampletransactionhash\"\n", - "transaction_on_network = api.await_transaction_on_condition(\n", - " transaction_hash=tx_hash, condition=condition_to_be_satisfied)" + "transaction_on_network = api.await_transaction_on_condition(transaction_hash=tx_hash, condition=condition_to_be_satisfied)" ] }, { @@ -909,7 +960,7 @@ "source": [ "## Creating transactions\n", "\n", - "In this section, we'll learn how to create different types of transactions. For creating transactions, we can use `controllers` or `factories`. The `controllers` can be used for scripts or quick network interactions, while the `factories` provide a more granular approach. Usually, the `controllers` use the same parameters as the `factories` but also take an `Account` and the `nonce` of the sender as arguments. The `controllers` also hold some extra functionality, like waiting for transaction completion and parsing transactions. The same functionality can be obtained for transactions built using the `factories` as well, we'll see how in the sections bellow." + "In this section, we'll learn how to create different types of transactions. For creating transactions, we can use `controllers` or `factories`. The `controllers` can be used for scripts or quick network interactions, while the `factories` provide a more granular approach, usually needed for DApps. Usually, the `controllers` use the same parameters as the `factories` but also take an `Account` and the `nonce` of the sender as arguments. The `controllers` also hold some extra functionality, like waiting for transaction completion and parsing transactions. The same functionality can be obtained for transactions built using the `factories` as well, we'll see how in the sections bellow. In the following section we'll learn how to create transactions using both." ] }, { @@ -957,7 +1008,7 @@ "source": [ "#### Native token transfers using the controller\n", "\n", - "Because we'll use an `Account`, the transaction we'll be signed." + "Because we'll use an `Account`, the transaction will be signed." ] }, { @@ -983,14 +1034,20 @@ "transaction = transfers_controller.create_transaction_for_transfer(\n", " sender=account,\n", " nonce=account.get_nonce_then_increment(),\n", - " receiver=Address.new_from_bech32(\n", - " \"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " native_transfer_amount=1000000000000000000, # 1 EGLD\n", ")\n", "\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you know youl'll only send native tokens, the same transaction can be created using the `create_transaction_for_native_token_transfer` method." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1035,6 +1092,13 @@ "tx_hash = entrypoint.send_transaction(transaction)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you know youl'll only send native tokens, the same transaction can be created using the `create_transaction_for_native_token_transfer` method." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1065,18 +1129,27 @@ "first_transfer = TokenTransfer(token=esdt, amount=1000000000)\n", "\n", "nft = Token(identifier=\"NFT-987654\", nonce=10)\n", - "second_transfer = TokenTransfer(token=nft, amount=1)\n", + "second_transfer = TokenTransfer(token=nft, amount=1) # when sending NFTs we set the amount to `1`\n", + "\n", + "sft = Token(identifier=\"SFT-123987\", nonce=10)\n", + "third_transfer = TokenTransfer(token=nft, amount=7)\n", "\n", "transfers_controller = entrypoint.create_transfers_controller()\n", "transaction = transfers_controller.create_transaction_for_transfer(\n", " sender=alice,\n", " nonce=alice.get_nonce_then_increment(),\n", - " receiver=Address.new_from_bech32(\n", - " \"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", - " token_transfers=[first_transfer, second_transfer]\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " token_transfers=[first_transfer, second_transfer, third_transfer]\n", ")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you know youl'll only send ESDT tokens, the same transaction can be created using `create_transaction_for_esdt_token_transfer`." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1106,16 +1179,19 @@ "\n", "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", "\n", - "esdt = Token(identifier=\"TEST-123456\")\n", - "first_transfer = TokenTransfer(token=esdt, amount=1000000000)\n", + "esdt = Token(identifier=\"TEST-123456\") # fungible tokens don't have nonce\n", + "first_transfer = TokenTransfer(token=esdt, amount=1000000000) # we set the desired amount we want to send\n", "\n", "nft = Token(identifier=\"NFT-987654\", nonce=10)\n", - "second_transfer = TokenTransfer(token=nft, amount=1)\n", + "second_transfer = TokenTransfer(token=nft, amount=1) # when sending NFTs we set the amount to `1`\n", + "\n", + "sft = Token(identifier=\"SFT-123987\", nonce=10)\n", + "third_transfer = TokenTransfer(token=nft, amount=7) # for SFTs we set the desired amount we want to send\n", "\n", "transaction = factory.create_transaction_for_transfer(\n", " sender=alice.address,\n", " receiver=bob,\n", - " token_transfers=[first_transfer, second_transfer]\n", + " token_transfers=[first_transfer, second_transfer, third_transfer]\n", ")\n", "\n", "# set the sender's nonce\n", @@ -1131,7 +1207,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Also, sending both native and custom tokens is now supported. We can send both types of tokens using either the `controller` or the `factory`, but we'll use the controller for the sake of simplicity." + "If you know youl'll only send ESDT tokens, the same transaction can be created using `create_transaction_for_esdt_token_transfer`.\n", + "\n", + "#### Sending native and custom tokens\n", + "\n", + "Also, sending both native and custom tokens is now supported. If a `native_amount` is provided together with `token_transfers`, the native token will also be included in the `MultiESDTNFTTrasfer` built-in function call.\n", + "\n", + "We can send both types of tokens using either the `controller` or the `factory`, but we'll use the controller for the sake of simplicity." ] }, { @@ -1163,8 +1245,7 @@ "transaction = transfers_controller.create_transaction_for_transfer(\n", " sender=account,\n", " nonce=account.get_nonce_then_increment(),\n", - " receiver=Address.new_from_bech32(\n", - " \"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " native_transfer_amount=1000000000000000000, # 1 EGLD\n", " token_transfers=[first_transfer, second_transfer]\n", ")\n", @@ -1172,6 +1253,34 @@ "tx_hash = entrypoint.send_transaction(transaction)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Decoding transaction data\n", + "\n", + "For example, when sending multiple ESDT and NFT tokens, the receiver field of the transaction is the same as the sender field and also the value is set to `0` because all the information is encoded in the `data` field of the transaction.\n", + "\n", + "For decoding the data field we have a so called `TransactionDecoder`. We fetch the transaction from the network and then use the decoder." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import DevnetEntrypoint, TransactionDecoder\n", + "\n", + "entrypoint = DevnetEntrypoint()\n", + "transaction = entrypoint.get_transaction(\"3e7b39f33f37716186b6ffa8761d066f2139bff65a1075864f612ca05c05c05d\")\n", + "\n", + "decoder = TransactionDecoder()\n", + "decoded_transaction = decoder.get_transaction_metadata(transaction)\n", + "\n", + "print(decoded_transaction.to_dict())" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1191,6 +1300,7 @@ "metadata": {}, "outputs": [], "source": [ + "from pathlib import Path\n", "from multiversx_sdk.abi import Abi\n", "\n", "abi = Abi.load(Path(\"./contracts/adder.abi.json\"))" @@ -1239,7 +1349,7 @@ "\n", "When creating transactions that interact with smart contracts, we should provide the ABI file to the `controller` or `factory` if possible, so we can pass the arguments as native values. If the abi is not provided and we know what types the contract expects, we can pass the arguments as `typed values` (ex: BigUIntValue, ListValue, StructValue, etc.) or `bytes`.\n", "\n", - "We'll first create a transaction for deploying a smart contract using the `controller`." + "#### Deploying a smart contract using the controller" ] }, { @@ -1381,7 +1491,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, let's create the same transaction to deploy a contract using the `factory`. Keep in mind that, after the transaction is created the `nonce` needs to be properly set and the transaction should be signed before broadcasting it." + "#### Deploying a smart contract using the factory\n", + "\n", + "After the transaction is created the `nonce` needs to be properly set and the transaction should be signed before broadcasting it." ] }, { @@ -1411,8 +1523,7 @@ "# Or use simple, plain Python values and objects if you have provided an ABI to the factory:\n", "args = [42]\n", "\n", - "alice_address = Address.new_from_bech32(\n", - " \"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "alice_address = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", "\n", "deploy_transaction = factory.create_transaction_for_deploy(\n", " sender=alice_address,\n", @@ -1463,7 +1574,7 @@ "\n", "In this section we'll see how we can call an endpoint of our previoulsy deployed smart contract using both approaches with the `controller` and the `factory`.\n", "\n", - "Let's create a smart contract call transaction using the `controller`." + "#### Calling a smart contract using the controller" ] }, { @@ -1493,8 +1604,7 @@ "entrypoint = DevnetEntrypoint()\n", "controller = entrypoint.create_smart_contract_controller(abi=abi)\n", "\n", - "contract_address = Address.new_from_bech32(\n", - " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "# For deploy arguments, use typed value objects if you haven't provided an ABI\n", "args = [BigUIntValue(42)]\n", @@ -1519,6 +1629,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "#### Parsing smart contract call transactions\n", + "\n", "In our case, calling the `add` endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call." ] }, @@ -1538,6 +1650,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "#### Calling a smart contract and sending tokens (transfer & execute)\n", + "\n", "Aditionally, if our endpoint requires a payment when called, we can also send tokens to the contract when creating a smart contract call transaction. We can send EGLD, ESDT tokens or both. This is supported both on the `controller` and the `factory`." ] }, @@ -1568,8 +1682,7 @@ "entrypoint = DevnetEntrypoint()\n", "controller = entrypoint.create_smart_contract_controller(abi=abi)\n", "\n", - "contract_address = Address.new_from_bech32(\n", - " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "# For deploy arguments, use typed value objects if you haven't provided an ABI\n", "args = [BigUIntValue(42)]\n", @@ -1603,7 +1716,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, let's create the same smart contract call transaction, but using the `factory`." + "#### Calling a smart contract using the factory\n", + "\n", + "Let's create the same smart contract call transaction, but using the `factory`." ] }, { @@ -1633,8 +1748,7 @@ "entrypoint = DevnetEntrypoint()\n", "factory = entrypoint.create_smart_contract_transactions_factory(abi=abi)\n", "\n", - "contract_address = Address.new_from_bech32(\n", - " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "# For deploy arguments, use typed value objects if you haven't provided an ABI to the factory:\n", "args = [BigUIntValue(42)]\n", @@ -1672,7 +1786,7 @@ "source": [ "#### Parsing transaction outcome\n", "\n", - "As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of the transaction, as follows:" + "As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of smart contract call transactions, as follows:" ] }, { @@ -1691,9 +1805,7 @@ "parser = SmartContractTransactionsOutcomeParser(abi=abi)\n", "\n", "# fetch the transaction of the network\n", - "network_provider = DevnetEntrypoint().network_provider # expose get_transaction in entrypoint\n", - "transaction_on_network = network_provider.get_transaction(\n", - " tx_hash) # the tx_hash from the transaction sent above\n", + "transaction_on_network = entrypoint.get_transaction(tx_hash) # the tx_hash from the transaction sent above\n", "\n", "outcome = parser.parse_execute(transaction=transaction_on_network, function=\"add\")" ] @@ -1724,7 +1836,7 @@ "abi = Abi.load(Path(\"contracts/multisig-full.abi.json\"))\n", "\n", "# fetch the transaction of the network\n", - "network_provider = DevnetEntrypoint().network_provider # expose get_transaction in entrypoint\n", + "network_provider = DevnetEntrypoint().create_network_provider()\n", "transaction_on_network = network_provider.get_transaction(tx_hash)\n", "\n", "# extract the event from the transaction\n", @@ -1761,8 +1873,7 @@ "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# the contract address we'll query\n", - "contract_address = Address.new_from_bech32(\n", - " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "# create the controller\n", "sc_controller = DevnetEntrypoint().create_smart_contract_controller(abi=abi)\n", @@ -1795,8 +1906,7 @@ "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# the contract address we'll query\n", - "contract_address = Address.new_from_bech32(\n", - " \"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", + "contract_address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug\")\n", "\n", "# create the controller\n", "sc_controller = DevnetEntrypoint().create_smart_contract_controller(abi=abi)\n", @@ -1823,7 +1933,7 @@ "\n", "Contract upgrade transactions are similar to deployment transactions (see above), in the sense that they also require a contract bytecode. In this context though, the contract address is already known. Similar to deploying a smart contract, we can upgrade a smart contract using either the `controller` or the `factory`.\n", "\n", - "We'll first upgrade our smart contract using the `controller`." + "#### Uprgrading a smart contract using the controller" ] }, { @@ -1886,7 +1996,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, let's create the same upgrade transaction using the `factory`." + "#### Upgrading a smart contract using the factory\n", + "\n", + "Let's create the same upgrade transaction using the `factory`." ] }, { @@ -1950,7 +2062,8 @@ "deploy_transaction.signature = alice.sign_transaction(deploy_transaction)\n", "\n", "# broadcasting the transaction\n", - "tx_hash = entrypoint.send_transaction(deploy_transaction)" + "tx_hash = entrypoint.send_transaction(deploy_transaction)\n", + "print(tx_hash.hex())" ] }, { @@ -1963,7 +2076,7 @@ "\n", "Of course, the methods used here are available through the `TokenManagementController` or through the `TokenManagementTransactionsFactory`. The controller also contains methods for awaiting transaction completion and for parsing the transaction outcome. The same can be achieved for the transactions factory by using the `TokenManagementTransactionsOutcomeParser`. For scripts or quick network interactions we advise you use the controller, but for a more granular approach (e.g. DApps) we suggest using the factory.\n", "\n", - "#### Issuing fungible tokens" + "#### Issuing fungible tokens using the controller" ] }, { @@ -1988,9 +2101,9 @@ "transaction = controller.create_transaction_for_issuing_fungible(\n", " sender=alice,\n", " nonce=alice.get_nonce_then_increment(),\n", - " token_name=\"NEWTOKEN\",\n", - " token_ticker=\"TKN\",\n", - " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", + " token_name=\"NEWFNG\",\n", + " token_ticker=\"FNG\",\n", + " initial_supply=1_000_000_000000, # 1 million tokens, with 6 decimals\n", " num_decimals=6,\n", " can_freeze=False,\n", " can_wipe=True,\n", @@ -2013,7 +2126,64 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Setting special roles for fungible tokens" + "#### Issuing fungible tokens using the factory" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser\n", + "\n", + "# create the entrypoint and the token management transactions factory\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_token_management_transactions_factory()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "transaction = factory.create_transaction_for_issuing_fungible(\n", + " sender=alice.address,\n", + " token_name=\"NEWFNG\",\n", + " token_ticker=\"FNG\",\n", + " initial_supply=1_000_000_000000, # 1 million tokens, with 6 decimals\n", + " num_decimals=6,\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True\n", + ")\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)`\n", + "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "\n", + "# extract the token identifier\n", + "parser = TokenManagementTransactionsOutcomeParser()\n", + "outcome = parser.parse_issue_fungible(transaction_on_network)\n", + "\n", + "token_identifier = outcome[0].token_identifier" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Setting special roles for fungible tokens using the controller" ] }, { @@ -2061,7 +2231,60 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Issuing semi-fungible tokens" + "#### Setting special roles for fungible tokens using the factory" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser\n", + "\n", + "# create the entrypoint and the token management transactions factory\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_token_management_transactions_factory()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "transaction = factory.create_transaction_for_setting_special_role_on_fungible_token(\n", + " sender=alice.address,\n", + " user=bob,\n", + " token_identifier=\"TEST-123456\",\n", + " add_role_local_mint=True,\n", + " add_role_local_burn=True,\n", + " add_role_esdt_transfer_role=True\n", + ")\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# waits until the transaction is processed and fetches it from the network\n", + "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "\n", + "# extract the roles\n", + "parser = TokenManagementTransactionsOutcomeParser()\n", + "outcome = parser.parse_set_special_role(transaction_on_network)\n", + "\n", + "roles = outcome[0].roles\n", + "uaser = outcome[0].user_address" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Issuing semi-fungible tokens using the controller" ] }, { @@ -2086,8 +2309,8 @@ "transaction = controller.create_transaction_for_issuing_semi_fungible(\n", " sender=alice,\n", " nonce=alice.get_nonce_then_increment(),\n", - " token_name=\"NEWTOKEN\",\n", - " token_ticker=\"TKN\",\n", + " token_name=\"NEWSEMI\",\n", + " token_ticker=\"SEMI\",\n", " can_freeze=False,\n", " can_wipe=True,\n", " can_pause=False,\n", @@ -2110,7 +2333,63 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Issuing NFT collection & creating NFTs" + "#### Issuing semi-fungible tokens using the factory" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser\n", + "\n", + "# create the entrypoint and the token management transactions factory\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_token_management_transactions_factory()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "transaction = factory.create_transaction_for_issuing_semi_fungible(\n", + " sender=alice.address,\n", + " token_name=\"NEWSEMI\",\n", + " token_ticker=\"SEMI\",\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_transfer_nft_create_role=True,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True\n", + ")\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# waits until the transaction is processed and fetches it from the network\n", + "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "\n", + "# extract the token identifier\n", + "parser = TokenManagementTransactionsOutcomeParser()\n", + "outcome = parser.parse_issue_semi_fungible(transaction_on_network)\n", + "\n", + "token_identifier = outcome[0].token_identifier" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Issuing NFT collection & creating NFTs using the controller" ] }, { @@ -2132,11 +2411,12 @@ "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", + "# issue NFT collection\n", "transaction = controller.create_transaction_for_issuing_non_fungible(\n", " sender=alice,\n", " nonce=alice.get_nonce_then_increment(),\n", - " token_name=\"NEWTOKEN\",\n", - " token_ticker=\"TKN\",\n", + " token_name=\"NEWNFT\",\n", + " token_ticker=\"NFT\",\n", " can_freeze=False,\n", " can_wipe=True,\n", " can_pause=False,\n", @@ -2182,21 +2462,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "These are just a few examples of what we can do using the token management controller or factory. For a full list of what methods are supported for both, check out the autogenerated documentation:\n", - "- [TokenManagementController](#)\n", - "- [TokenManagementTransactionsFactory](#)\n", - "\n", - "### Account management\n", - "\n", - "The account management controller and factory allow us to create transactions for managing accounts, like guarding and unguarding accounts and saving key-value pairs.\n", - "\n", - "#### Guarding an account\n", - "\n", - "To read more about Guardians, check out the [documentation](https://docs.multiversx.com/developers/built-in-functions/#setguardian).\n", - "\n", - "A guardian can also be set using the WebWallet. The wallet uses our hosted `Trusted Co-Signer Service`. Check out the steps to guard an account using the wallet [here](https://docs.multiversx.com/wallet/web-wallet/#guardian).\n", - "\n", - "Keep in mind that, all the methods presented bellow are available in the `AccountManagementTransactionsFactory`, as well." + "#### Issuing NFT collection & creating NFTs using the factory" ] }, { @@ -2206,36 +2472,97 @@ "outputs": [], "source": [ "from pathlib import Path\n", - "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "from multiversx_sdk import Account, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the token management transactions factory\n", "entrypoint = DevnetEntrypoint()\n", - "controller = entrypoint.create_account_controller()\n", + "factory = entrypoint.create_token_management_transactions_factory()\n", "\n", - "# create the account to guard\n", + "# create the issuer of the token\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", + "# issue NFT collection\n", + "transaction = factory.create_transaction_for_issuing_non_fungible(\n", + " sender=alice.address,\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_transfer_nft_create_role=True,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True\n", + ")\n", + "\n", "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", "\n", - "# we can use a trusted service that provides a guardian, or simply set another address we own or trust\n", - "guardian = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", "\n", - "transaction = controller.create_transaction_for_setting_guardian(\n", - " sender=alice,\n", - " nonce=alice.get_nonce_then_increment(),\n", - " guardian_address=guardian,\n", - " service_id=\"SelfOwnedAddress\" # this is just an example\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# if we know that the transaction is completed, we can simply call `get_transaction(tx_hash)`\n", + "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "\n", + "# extract the collection identifier\n", + "parser = TokenManagementTransactionsOutcomeParser()\n", + "outcome = parser.parse_issue_non_fungible(transaction_on_network)\n", + "\n", + "collection_identifier = outcome[0].token_identifier\n", + "\n", + "# create a NFT\n", + "transaction = factory.create_transaction_for_creating_nft(\n", + " sender=alice.address,\n", + " token_identifier=collection_identifier,\n", + " initial_quantity=1,\n", + " name=\"TEST\",\n", + " royalties=2500, # 25%\n", + " hash=\"\",\n", + " attributes=b\"\",\n", + " uris=[]\n", ")\n", + "# set the nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", "\n", - "tx_hash = entrypoint.send_transaction(transaction)" + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# waits until the transaction is processed and fetches it from the network\n", + "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "\n", + "# extract the nft identifier\n", + "parser = TokenManagementTransactionsOutcomeParser()\n", + "outcome = parser.parse_nft_create(transaction_on_network)\n", + "\n", + "identifier = outcome[0].token_identifier\n", + "nonce = outcome[0].nonce\n", + "initial_quantity = outcome[0].initial_quantity" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "After we've set a guardian, we have to wait 20 epochs until we can activate the guardian. After the guardian is set, all the transactions we send should be signed by the guardian, as well. To activate the guardian we can do as follows:" + "These are just a few examples of what we can do using the token management controller or factory. For a full list of what methods are supported for both, check out the autogenerated documentation:\n", + "- [TokenManagementController](#)\n", + "- [TokenManagementTransactionsFactory](#)\n", + "\n", + "### Account management\n", + "\n", + "The account management controller and factory allow us to create transactions for managing accounts, like guarding and unguarding accounts and saving key-value pairs.\n", + "\n", + "To read more about Guardians, check out the [documentation](https://docs.multiversx.com/developers/built-in-functions/#setguardian).\n", + "\n", + "A guardian can also be set using the WebWallet. The wallet uses our hosted `Trusted Co-Signer Service`. Check out the steps to guard an account using the wallet [here](https://docs.multiversx.com/wallet/web-wallet/#guardian).\n", + "\n", + "#### Guarding an account using the controller" ] }, { @@ -2245,9 +2572,9 @@ "outputs": [], "source": [ "from pathlib import Path\n", - "from multiversx_sdk import Account, DevnetEntrypoint\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account controller\n", "entrypoint = DevnetEntrypoint()\n", "controller = entrypoint.create_account_controller()\n", "\n", @@ -2257,9 +2584,14 @@ "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "transaction = controller.create_transaction_for_guarding_account(\n", + "# we can use a trusted service that provides a guardian, or simply set another address we own or trust\n", + "guardian = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "transaction = controller.create_transaction_for_setting_guardian(\n", " sender=alice,\n", - " nonce=alice.get_nonce_then_increment()\n", + " nonce=alice.get_nonce_then_increment(),\n", + " guardian_address=guardian,\n", + " service_id=\"SelfOwnedAddress\" # this is just an example\n", ")\n", "\n", "tx_hash = entrypoint.send_transaction(transaction)" @@ -2269,7 +2601,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If at some point, we want to disable our guardian, we can send the following transaction:" + "#### Guarding an account using the factory" ] }, { @@ -2281,25 +2613,31 @@ "from pathlib import Path\n", "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account transactions factory\n", "entrypoint = DevnetEntrypoint()\n", - "controller = entrypoint.create_account_controller()\n", + "factory = entrypoint.create_account_transactions_factory()\n", "\n", "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", + "# we can use a trusted service that provides a guardian, or simply set another address we own or trust\n", + "guardian = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "transaction = factory.create_transaction_for_setting_guardian(\n", + " sender=alice.address,\n", + " guardian_address=guardian,\n", + " service_id=\"SelfOwnedAddress\" # this is just an example\n", + ")\n", + "\n", "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "guardian = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "# set the nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", "\n", - "transaction = controller.create_transaction_for_unguarding_account(\n", - " sender=alice,\n", - " nonce=alice.get_nonce_then_increment(),\n", - " guardian=guardian\n", - ")\n", + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", "\n", - "# the transaction should also be signed by the guardian before being sent\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, @@ -2307,9 +2645,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Saving a key-value pair to an account\n", + "After we've set a guardian, we have to wait 20 epochs until we can activate the guardian. After the guardian is set, all the transactions we send should be signed by the guardian, as well.\n", "\n", - "We can store key-value pairs for an acoount on the network. To do so, we create the following transaction:" + "#### Activating the guardian using the controller" ] }, { @@ -2321,7 +2659,7 @@ "from pathlib import Path\n", "from multiversx_sdk import Account, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account controller\n", "entrypoint = DevnetEntrypoint()\n", "controller = entrypoint.create_account_controller()\n", "\n", @@ -2331,16 +2669,9 @@ "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "# creating the key-value pairs we want to save\n", - "values = {\n", - " \"testKey\".encode(): \"testValue\".encode(),\n", - " b\"anotherKey\": b\"anotherValue\"\n", - "}\n", - "\n", - "transaction = controller.create_transaction_for_saving_key_value(\n", + "transaction = controller.create_transaction_for_guarding_account(\n", " sender=alice,\n", - " nonce=alice.get_nonce_then_increment(),\n", - " key_value_pairs=values\n", + " nonce=alice.get_nonce_then_increment()\n", ")\n", "\n", "tx_hash = entrypoint.send_transaction(transaction)" @@ -2350,15 +2681,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Delegation management\n", - "\n", - "To read more about staking providers and delegation, please check out the [docs](https://docs.multiversx.com/validators/delegation-manager/#introducing-staking-providers). \n", - "\n", - "In this section, we are going to create a new delegation contract, get the address of the contract, delegate funds to the contract, redelegate rewards, claim rewards, undelegate and withdraw funds from the contract. The operations can be performed using both the `controller` and the `factory`. For a full list of all the methods supported check out the auto-generated documentation:\n", - "- [DelegationController](#)\n", - "- [DelegationTransactionsFactory](#)\n", - "\n", - "#### Creating a new delegation contract" + "#### Activating the guardian using the factory" ] }, { @@ -2368,41 +2691,39 @@ "outputs": [], "source": [ "from pathlib import Path\n", - "from multiversx_sdk import Account, DevnetEntrypoint\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account transactions factory\n", "entrypoint = DevnetEntrypoint()\n", - "controller = entrypoint.create_delegation_controller()\n", + "factory = entrypoint.create_account_transactions_factory()\n", "\n", "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", - "# fetch the nonce of the network\n", - "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "# we can use a trusted service that provides a guardian, or simply set another address we own or trust\n", + "guardian = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", "\n", - "transaction = controller.create_transaction_for_new_delegation_contract(\n", - " sender=alice,\n", - " nonce=alice.get_nonce_then_increment(),\n", - " total_delegation_cap=0, # uncapped,\n", - " service_fee=0,\n", - " amount=1250000000000000000000 # 1250 EGLD\n", + "transaction = factory.create_transaction_for_guarding_account(\n", + " sender=alice.address,\n", ")\n", "\n", - "tx_hash = entrypoint.send_transaction(transaction)\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "# wait for transaction completion, extract delegation contract's address\n", - "outcome = controller.await_completed_create_new_delegation_contract(tx_hash)\n", + "# set the nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", "\n", - "contract_address = outcome[0].contract_address" + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Delegating funds to the contract\n", - "\n", - "We can send funds to a delegation contract to earn rewards." + "#### Unguarding the account using the controller" ] }, { @@ -2414,9 +2735,9 @@ "from pathlib import Path\n", "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account controller\n", "entrypoint = DevnetEntrypoint()\n", - "controller = entrypoint.create_delegation_controller()\n", + "controller = entrypoint.create_account_controller()\n", "\n", "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", @@ -2424,16 +2745,15 @@ "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "# delegation contract\n", - "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "guardian = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", "\n", - "transaction = controller.create_transaction_for_delegating(\n", + "transaction = controller.create_transaction_for_unguarding_account(\n", " sender=alice,\n", " nonce=alice.get_nonce_then_increment(),\n", - " delegation_contract=contract,\n", - " amount=5000000000000000000000 # 5000 EGLD\n", + " guardian=guardian\n", ")\n", "\n", + "# the transaction should also be signed by the guardian before being sent otherwise it won't be executed\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, @@ -2441,9 +2761,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Redelegating rewards\n", - "\n", - "After a period of time, we might have enough rewards that we want to redelegate to the contract to earn even more rewards. " + "#### Unguarding the account using the factory" ] }, { @@ -2455,25 +2773,31 @@ "from pathlib import Path\n", "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account transactions factory\n", "entrypoint = DevnetEntrypoint()\n", - "controller = entrypoint.create_delegation_controller()\n", + "factory = entrypoint.create_account_transactions_factory()\n", "\n", "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", + "# we can use a trusted service that provides a guardian, or simply set another address we own or trust\n", + "guardian = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "transaction = factory.create_transaction_for_unguarding_account(\n", + " sender=alice.address,\n", + " guardian=guardian\n", + ")\n", + "\n", "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "# delegation contract\n", - "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "# set the nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", "\n", - "transaction = controller.create_transaction_for_redelegating_rewards(\n", - " sender=alice,\n", - " nonce=alice.get_nonce_then_increment(),\n", - " delegation_contract=contract\n", - ")\n", + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", "\n", + "# the transaction should also be signed by the guardian before being sent otherwise it won't be executed\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, @@ -2481,9 +2805,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Claiming rewards\n", + "#### Saving a key-value pair to an account using the controller\n", "\n", - "We can also claim our rewards." + "We can store key-value pairs for an acoount on the network. To do so, we create the following transaction:" ] }, { @@ -2493,11 +2817,11 @@ "outputs": [], "source": [ "from pathlib import Path\n", - "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account controller\n", "entrypoint = DevnetEntrypoint()\n", - "controller = entrypoint.create_delegation_controller()\n", + "controller = entrypoint.create_account_controller()\n", "\n", "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", @@ -2505,15 +2829,19 @@ "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "# delegation contract\n", - "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "# creating the key-value pairs we want to save\n", + "values = {\n", + " \"testKey\".encode(): \"testValue\".encode(),\n", + " b\"anotherKey\": b\"anotherValue\"\n", + "}\n", "\n", - "transaction = controller.create_transaction_for_claiming_rewards(\n", + "transaction = controller.create_transaction_for_saving_key_value(\n", " sender=alice,\n", " nonce=alice.get_nonce_then_increment(),\n", - " delegation_contract=contract\n", + " key_value_pairs=values\n", ")\n", "\n", + "# broadcast the transaction\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, @@ -2521,9 +2849,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Undelegating funds\n", - "\n", - "By undelegating we let the contract know we want to get back our staked funds. This operation has a 10 epochs unbonding period." + "#### Saving a key-value pair to an account using the factory" ] }, { @@ -2533,28 +2859,36 @@ "outputs": [], "source": [ "from pathlib import Path\n", - "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account transactions factory\n", "entrypoint = DevnetEntrypoint()\n", - "controller = entrypoint.create_delegation_controller()\n", + "factory = entrypoint.create_account_transactions_factory()\n", "\n", "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", + "# creating the key-value pairs we want to save\n", + "values = {\n", + " \"testKey\".encode(): \"testValue\".encode(),\n", + " b\"anotherKey\": b\"anotherValue\"\n", + "}\n", + "\n", + "transaction = factory.create_transaction_for_saving_key_value(\n", + " sender=alice.address,\n", + " key_value_pairs=values\n", + ")\n", + "\n", "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "# delegation contract\n", - "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "# set the nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", "\n", - "transaction = controller.create_transaction_for_undelegating(\n", - " sender=alice,\n", - " nonce=alice.get_nonce_then_increment(),\n", - " delegation_contract=contract,\n", - " amount=1000000000000000000000 # 1000 EGLD\n", - ")\n", + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", "\n", + "# broadcast the transaction\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, @@ -2562,9 +2896,15 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Withdrawing funds\n", + "### Delegation management\n", + "\n", + "To read more about staking providers and delegation, please check out the [docs](https://docs.multiversx.com/validators/delegation-manager/#introducing-staking-providers). \n", + "\n", + "In this section, we are going to create a new delegation contract, get the address of the contract, delegate funds to the contract, redelegate rewards, claim rewards, undelegate and withdraw funds from the contract. The operations can be performed using both the `controller` and the `factory`. For a full list of all the methods supported check out the auto-generated documentation:\n", + "- [DelegationController](#)\n", + "- [DelegationTransactionsFactory](#)\n", "\n", - "After the unbonding period has passed, we can withdraw our funds from the contract" + "#### Creating a new delegation contract using the controller" ] }, { @@ -2574,9 +2914,9 @@ "outputs": [], "source": [ "from pathlib import Path\n", - "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account controller\n", "entrypoint = DevnetEntrypoint()\n", "controller = entrypoint.create_delegation_controller()\n", "\n", @@ -2586,25 +2926,27 @@ "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "# delegation contract\n", - "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", - "\n", - "transaction = controller.create_transaction_for_withdrawing(\n", + "transaction = controller.create_transaction_for_new_delegation_contract(\n", " sender=alice,\n", " nonce=alice.get_nonce_then_increment(),\n", - " delegation_contract=contract\n", + " total_delegation_cap=0, # uncapped,\n", + " service_fee=0,\n", + " amount=1250000000000000000000 # 1250 EGLD\n", ")\n", "\n", - "tx_hash = entrypoint.send_transaction(transaction)" - ] - }, + "tx_hash = entrypoint.send_transaction(transaction)\n", + "\n", + "# wait for transaction completion, extract delegation contract's address\n", + "outcome = controller.await_completed_create_new_delegation_contract(tx_hash)\n", + "\n", + "contract_address = outcome[0].contract_address" + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Relayed transactions\n", - "\n", - "We are currently on the third iteration of relayed transactions. V1 and V2 are soon to be deactivated so we'll focus on V3. For V3, two new fields have been added on transactions: `releyer` and `relayerSignature`. Before the sender signs the transaction, the relayer needs to be set. After the sender has signed the transaction, the relayer can also sign the transaction and broadcast it. Keep in mind that, for relayed V3 transactions we need an extra `50_000` gas. Let's see how we can create a relayed transaction:" + "#### Creating a new delegation contract using the factory" ] }, { @@ -2614,38 +2956,82 @@ "outputs": [], "source": [ "from pathlib import Path\n", - "from multiversx_sdk import Account, Address, DevnetEntrypoint, Transaction\n", + "from multiversx_sdk import Account, DevnetEntrypoint, DelegationTransactionsOutcomeParser\n", + "\n", + "# create the entrypoint and the account transactions factory\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_delegation_transactions_factory()\n", "\n", "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", - "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", - "\n", - "# carol will be our relayer, that means she is paying the gas for the transaction\n", - "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "transaction = factory.create_transaction_for_new_delegation_contract(\n", + " sender=alice.address,\n", + " total_delegation_cap=0, # uncapped,\n", + " service_fee=0,\n", + " amount=1250000000000000000000 # 1250 EGLD\n", + ")\n", "\n", - "# fetch the sender's nonce of the network\n", + "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "# create the transaction\n", - "transaction = Transaction(\n", - " sender=alice.address,\n", - " receiver=bob,\n", - " gas_limit=110_000,\n", - " chain_id=\"D\",\n", - " nonce=alice.get_nonce_then_increment(),\n", - " relayer=carol.address,\n", - " data=\"hello\".encode()\n", - ")\n", + "# set the nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", "\n", - "# sender signs the transaction\n", + "# sign the transaction\n", "transaction.signature = alice.sign_transaction(transaction)\n", "\n", - "# relayer signs the transaction\n", - "transaction.relayer_signature = carol.sign_transaction(transaction)\n", + "# send the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)\n", "\n", - "# broadcast the transcation\n", + "# waits until the transaction is processed and fetches it from the network\n", + "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "\n", + "# extract the contract's address\n", + "parser = DelegationTransactionsOutcomeParser()\n", + "outcome = parser.parse_create_new_delegation_contract(transaction_on_network)\n", + "\n", + "contract_address = outcome[0].contract_address" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Delegating funds to the contract using the controller\n", + "\n", + "We can send funds to a delegation contract to earn rewards." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the account controller\n", "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_delegation_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# delegation contract\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "\n", + "transaction = controller.create_transaction_for_delegating(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " delegation_contract=contract,\n", + " amount=5000000000000000000000 # 5000 EGLD\n", + ")\n", + "\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, @@ -2653,9 +3039,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Creating relayed transactions using controllers\n", - "\n", - "We can create relayed transactions using any of the controllers. Each controller has a `relayer` argument, that can be set if we want to create a relayed transaction. Let's issue a fungible token creating a relayed transaction:" + "#### Delegating funds to the contract using the factory" ] }, { @@ -2667,39 +3051,69 @@ "from pathlib import Path\n", "from multiversx_sdk import Account, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account transactions factory\n", "entrypoint = DevnetEntrypoint()\n", - "controller = entrypoint.create_token_management_controller()\n", + "factory = entrypoint.create_delegation_transactions_factory()\n", "\n", - "# create the issuer of the token\n", + "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", - "# carol will be our relayer, that means she is paying the gas for the transaction\n", - "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "transaction = factory.create_transaction_for_delegating(\n", + " sender=alice.address,\n", + " delegation_contract=contract,\n", + " amount=5000000000000000000000 # 5000 EGLD\n", + ")\n", "\n", "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "transaction = controller.create_transaction_for_issuing_fungible(\n", + "# set the nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# send the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Redelegating rewards using the controller\n", + "\n", + "After a period of time, we might have enough rewards that we want to redelegate to the contract to earn even more rewards. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the account controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_delegation_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# delegation contract\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "\n", + "transaction = controller.create_transaction_for_redelegating_rewards(\n", " sender=alice,\n", " nonce=alice.get_nonce_then_increment(),\n", - " token_name=\"NEWTOKEN\",\n", - " token_ticker=\"TKN\",\n", - " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", - " num_decimals=6,\n", - " can_freeze=False,\n", - " can_wipe=True,\n", - " can_pause=False,\n", - " can_change_owner=True,\n", - " can_upgrade=True,\n", - " can_add_special_roles=True,\n", - " relayer=carol.address\n", + " delegation_contract=contract\n", ")\n", "\n", - "# relayer also signs the transaction\n", - "transaction.relayer_signature = carol.sign_transaction(transaction)\n", - "\n", - "# sending the transaction\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, @@ -2707,9 +3121,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Create relayed transactions using factories\n", - "\n", - "The transactions factories do not have a `relayer` argument, the relayer needs to be set after creating the transaction. This is good because the transaction is not signed by the sender when created. Let's issue a fungible token using the `TokenManagementTransactionsFactory`:" + "#### Redelegating rewards using the factory" ] }, { @@ -2721,46 +3133,28 @@ "from pathlib import Path\n", "from multiversx_sdk import Account, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management factory\n", + "# create the entrypoint and the account transactions factory\n", "entrypoint = DevnetEntrypoint()\n", - "factory = entrypoint.create_token_management_transactions_factory()\n", + "factory = entrypoint.create_delegation_transactions_factory()\n", "\n", - "# create the issuer of the token\n", + "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", - "# carol will be our relayer, that means she is paying the gas for the transaction\n", - "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", - "\n", - "transaction = factory.create_transaction_for_issuing_fungible(\n", + "transaction = factory.create_transaction_for_redelegating_rewards(\n", " sender=alice.address,\n", - " token_name=\"NEWTOKEN\",\n", - " token_ticker=\"TKN\",\n", - " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", - " num_decimals=6,\n", - " can_freeze=False,\n", - " can_wipe=True,\n", - " can_pause=False,\n", - " can_change_owner=True,\n", - " can_upgrade=True,\n", - " can_add_special_roles=True\n", + " delegation_contract=contract\n", ")\n", "\n", "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "# set the nonce of the sender\n", + "# set the nonce\n", "transaction.nonce = alice.get_nonce_then_increment()\n", "\n", - "# set the relayer\n", - "transaction.relayer = carol.address\n", - "\n", - "# sender signs the transaction\n", + "# sign the transaction\n", "transaction.signature = alice.sign_transaction(transaction)\n", "\n", - "# relayer signs the transaction\n", - "transaction.relayer_signature = carol.sign_transaction(transaction)\n", - "\n", - "# broadcast the transaction\n", + "# send the transaction\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, @@ -2768,9 +3162,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Creating guarded transactions using controllers\n", + "#### Claiming rewards using the controller\n", "\n", - "Very similar to relayers, we have a field `guardian` and a field `guardianSignature`. Each controller has an argument for the guardian. The transaction can be sent to a service that signs it using the guardian's account or we can use another account as a guardian. Let's issue a token using a guarded account." + "We can also claim our rewards." ] }, { @@ -2780,41 +3174,27 @@ "outputs": [], "source": [ "from pathlib import Path\n", - "from multiversx_sdk import Account, DevnetEntrypoint\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management controller\n", + "# create the entrypoint and the account controller\n", "entrypoint = DevnetEntrypoint()\n", - "controller = entrypoint.create_token_management_controller()\n", + "controller = entrypoint.create_delegation_controller()\n", "\n", - "# create the issuer of the token\n", + "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", - "# carol is the guardian\n", - "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", - "\n", "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "transaction = controller.create_transaction_for_issuing_fungible(\n", + "# delegation contract\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "\n", + "transaction = controller.create_transaction_for_claiming_rewards(\n", " sender=alice,\n", " nonce=alice.get_nonce_then_increment(),\n", - " token_name=\"NEWTOKEN\",\n", - " token_ticker=\"TKN\",\n", - " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", - " num_decimals=6,\n", - " can_freeze=False,\n", - " can_wipe=True,\n", - " can_pause=False,\n", - " can_change_owner=True,\n", - " can_upgrade=True,\n", - " can_add_special_roles=True,\n", - " guardian=carol.address\n", + " delegation_contract=contract\n", ")\n", "\n", - "# guardian also signs the transaction\n", - "transaction.guardian_signature = carol.sign_transaction(transaction)\n", - "\n", - "# sending the transaction\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, @@ -2822,9 +3202,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Creating guarded transactions using factories\n", - "\n", - "The transactions factories do not have a `guardian` argument, the guardian needs to be set after creating the transaction. This is good because the transaction is not signed by the sender when created. Let's issue a fungible token using the `TokenManagementTransactionsFactory`:" + "#### Claiming rewards using the factory" ] }, { @@ -2836,46 +3214,28 @@ "from pathlib import Path\n", "from multiversx_sdk import Account, DevnetEntrypoint\n", "\n", - "# create the entrypoint and the token management factory\n", + "# create the entrypoint and the account transactions factory\n", "entrypoint = DevnetEntrypoint()\n", - "factory = entrypoint.create_token_management_transactions_factory()\n", + "factory = entrypoint.create_delegation_transactions_factory()\n", "\n", - "# create the issuer of the token\n", + "# create the account to guard\n", "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", - "# carol is the guardian\n", - "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", - "\n", - "transaction = factory.create_transaction_for_issuing_fungible(\n", + "transaction = factory.create_transaction_for_claiming_rewards(\n", " sender=alice.address,\n", - " token_name=\"NEWTOKEN\",\n", - " token_ticker=\"TKN\",\n", - " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", - " num_decimals=6,\n", - " can_freeze=False,\n", - " can_wipe=True,\n", - " can_pause=False,\n", - " can_change_owner=True,\n", - " can_upgrade=True,\n", - " can_add_special_roles=True\n", + " delegation_contract=contract\n", ")\n", "\n", "# fetch the nonce of the network\n", "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", "\n", - "# set the nonce of the sender\n", + "# set the nonce\n", "transaction.nonce = alice.get_nonce_then_increment()\n", "\n", - "# set the guardian\n", - "transaction.guardian = carol.address\n", - "\n", - "# sender signs the transaction\n", + "# sign the transaction\n", "transaction.signature = alice.sign_transaction(transaction)\n", "\n", - "# guardian signs the transaction\n", - "transaction.guardian_signature = carol.sign_transaction(transaction)\n", - "\n", - "# broadcast the transaction\n", + "# send the transaction\n", "tx_hash = entrypoint.send_transaction(transaction)" ] }, @@ -2883,7 +3243,1124 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We can also create guarded relayed transactions the same way we did before. Keep in mind that, only the sender can be guarded, the relayer cannot. The same flow can be used. Using controllers, we set both `guardian` and `relayer` fields and then the transaction should be signed by both. Using a factory, we create the transaction, set both both fields and then sign the transaction using the sender's account, then the the guardian and the relayer sign the transaction." + "#### Undelegating funds using the controller\n", + "\n", + "By undelegating we let the contract know we want to get back our staked funds. This operation has a 10 epochs unbonding period." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the account controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_delegation_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# delegation contract\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "\n", + "transaction = controller.create_transaction_for_undelegating(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " delegation_contract=contract,\n", + " amount=1000000000000000000000 # 1000 EGLD\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Undelegating funds using the factory" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the account transactions factory\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_delegation_transactions_factory()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "transaction = factory.create_transaction_for_undelegating(\n", + " sender=alice.address,\n", + " delegation_contract=contract,\n", + " amount=1000000000000000000000 # 1000 EGLD\n", + ")\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# set the nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# send the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Withdrawing funds using the controller\n", + "\n", + "After the unbonding period has passed, we can withdraw our funds from the contract." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the account controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_delegation_controller()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# delegation contract\n", + "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva\")\n", + "\n", + "transaction = controller.create_transaction_for_withdrawing(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " delegation_contract=contract\n", + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Withdrawing funds using the factory" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the account transactions factory\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_delegation_transactions_factory()\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "transaction = factory.create_transaction_for_withdrawing(\n", + " sender=alice.address,\n", + " delegation_contract=contract\n", + ")\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# set the nonce\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# sign the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# send the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Relayed transactions\n", + "\n", + "We are currently on the third iteration of relayed transactions. V1 and V2 are soon to be deactivated so we'll focus on V3. For V3, two new fields have been added on transactions: `releyer` and `relayerSignature`. Before the sender signs the transaction, the relayer needs to be set. After the sender has signed the transaction, the relayer can also sign the transaction and broadcast it. Keep in mind that, for relayed V3 transactions we need an extra `50_000` gas. Let's see how we can create a relayed transaction:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, DevnetEntrypoint, Transaction\n", + "\n", + "# create the account to guard\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "bob = Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\")\n", + "\n", + "# carol will be our relayer, that means she is paying the gas for the transaction\n", + "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "\n", + "# fetch the sender's nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# create the transaction\n", + "transaction = Transaction(\n", + " sender=alice.address,\n", + " receiver=bob,\n", + " gas_limit=110_000,\n", + " chain_id=\"D\",\n", + " nonce=alice.get_nonce_then_increment(),\n", + " relayer=carol.address,\n", + " data=\"hello\".encode()\n", + ")\n", + "\n", + "# sender signs the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# relayer signs the transaction\n", + "transaction.relayer_signature = carol.sign_transaction(transaction)\n", + "\n", + "# broadcast the transcation\n", + "entrypoint = DevnetEntrypoint()\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Creating relayed transactions using controllers\n", + "\n", + "We can create relayed transactions using any of the controllers. Each controller has a `relayer` argument, that can be set if we want to create a relayed transaction. Let's issue a fungible token creating a relayed transaction:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_token_management_controller()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# carol will be our relayer, that means she is paying the gas for the transaction\n", + "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "transaction = controller.create_transaction_for_issuing_fungible(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " initial_supply=1_000_000_000000, # 1 million tokens, with 6 decimals\n", + " num_decimals=6,\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True,\n", + " relayer=carol.address\n", + ")\n", + "\n", + "# relayer also signs the transaction\n", + "transaction.relayer_signature = carol.sign_transaction(transaction)\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Create relayed transactions using factories\n", + "\n", + "The transactions factories do not have a `relayer` argument, the relayer needs to be set after creating the transaction. This is good because the transaction is not signed by the sender when created. Let's issue a fungible token using the `TokenManagementTransactionsFactory`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management transactions factory\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_token_management_transactions_factory()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# carol will be our relayer, that means she is paying the gas for the transaction\n", + "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "\n", + "transaction = factory.create_transaction_for_issuing_fungible(\n", + " sender=alice.address,\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", + " num_decimals=6,\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True\n", + ")\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# set the nonce of the sender\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# set the relayer\n", + "transaction.relayer = carol.address\n", + "\n", + "# sender signs the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# relayer signs the transaction\n", + "transaction.relayer_signature = carol.sign_transaction(transaction)\n", + "\n", + "# broadcast the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Creating guarded transactions using controllers\n", + "\n", + "Very similar to relayers, we have a field `guardian` and a field `guardianSignature`. Each controller has an argument for the guardian. The transaction can be sent to a service that signs it using the guardian's account or we can use another account as a guardian. Let's issue a token using a guarded account." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management controller\n", + "entrypoint = DevnetEntrypoint()\n", + "controller = entrypoint.create_token_management_controller()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# carol is the guardian\n", + "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "transaction = controller.create_transaction_for_issuing_fungible(\n", + " sender=alice,\n", + " nonce=alice.get_nonce_then_increment(),\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", + " num_decimals=6,\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True,\n", + " guardian=carol.address\n", + ")\n", + "\n", + "# guardian also signs the transaction\n", + "transaction.guardian_signature = carol.sign_transaction(transaction)\n", + "\n", + "# sending the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Creating guarded transactions using factories\n", + "\n", + "The transactions factories do not have a `guardian` argument, the guardian needs to be set after creating the transaction. This is good because the transaction is not signed by the sender when created. Let's issue a fungible token using the `TokenManagementTransactionsFactory`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, DevnetEntrypoint\n", + "\n", + "# create the entrypoint and the token management transactions factory\n", + "entrypoint = DevnetEntrypoint()\n", + "factory = entrypoint.create_token_management_transactions_factory()\n", + "\n", + "# create the issuer of the token\n", + "alice = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# carol is the guardian\n", + "carol = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/carol.pem\"))\n", + "\n", + "transaction = factory.create_transaction_for_issuing_fungible(\n", + " sender=alice.address,\n", + " token_name=\"NEWTOKEN\",\n", + " token_ticker=\"TKN\",\n", + " initial_supply=1000000000000, # 1 million tokens, with 6 decimals\n", + " num_decimals=6,\n", + " can_freeze=False,\n", + " can_wipe=True,\n", + " can_pause=False,\n", + " can_change_owner=True,\n", + " can_upgrade=True,\n", + " can_add_special_roles=True\n", + ")\n", + "\n", + "# fetch the nonce of the network\n", + "alice.nonce = entrypoint.recall_account_nonce(alice.address)\n", + "\n", + "# set the nonce of the sender\n", + "transaction.nonce = alice.get_nonce_then_increment()\n", + "\n", + "# set the guardian\n", + "transaction.guardian = carol.address\n", + "\n", + "# sender signs the transaction\n", + "transaction.signature = alice.sign_transaction(transaction)\n", + "\n", + "# guardian signs the transaction\n", + "transaction.guardian_signature = carol.sign_transaction(transaction)\n", + "\n", + "# broadcast the transaction\n", + "tx_hash = entrypoint.send_transaction(transaction)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also create guarded relayed transactions the same way we did before. Keep in mind that, only the sender can be guarded, the relayer cannot. The same flow can be used. Using controllers, we set both `guardian` and `relayer` fields and then the transaction should be signed by both. Using a factory, we create the transaction, set both both fields and then sign the transaction using the sender's account, then the the guardian and the relayer sign the transaction." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Addresses\n", + "\n", + "Create an `Address` object from a _bech32-encoded_ string:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address\n", + "\n", + "address = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "\n", + "print(\"Address (bech32-encoded)\", address.to_bech32())\n", + "print(\"Public key (hex-encoded):\", address.to_hex())\n", + "print(\"Public key (hex-encoded):\", address.get_public_key().hex())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create an address from a _hex-encoded_ string - note that you have to provide the address prefix, also known as the **HRP** (_human-readable part_ of the address). If not provided, the default one will be used." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address\n", + "\n", + "address = Address.new_from_hex(\"0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1\", \"erd\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create an address from a raw public key:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address\n", + "\n", + "pubkey = bytes.fromhex(\"0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1\")\n", + "address = Address(pubkey, \"erd\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Alternatively, you can use an `AddressFactory` (initialized with a specific **HRP**) to create addresses. If the hrp is not provided, the default one will be used." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import AddressFactory\n", + "\n", + "factory = AddressFactory(\"erd\")\n", + "\n", + "address = factory.create_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "address = factory.create_from_hex(\"0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1\")\n", + "address = factory.create_from_public_key(bytes.fromhex(\"0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Getting the shard of an address" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, AddressComputer\n", + "\n", + "address_computer = AddressComputer()\n", + "address = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "\n", + "print(\"Shard:\", address_computer.get_shard_of_address(address))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Checking if the address is a smart contract address" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address\n", + "\n", + "address = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm\")\n", + "print(\"Is contract address:\", address.is_smart_contract())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Changing the default hrp\n", + "\n", + "We have a configuration class, called `LibraryConfig`, that only stores (for the moment) the **default hrp** of the addresses. The default value is `erd`. The hrp can be changed when instantiating an address, or it can be changed in the `LibraryConfig` class, and all the addresses created will have the newly set hrp." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address\n", + "from multiversx_sdk import LibraryConfig\n", + "\n", + "\n", + "print(LibraryConfig.default_address_hrp)\n", + "address = Address.new_from_hex(\"0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1\")\n", + "print(address.to_bech32())\n", + "\n", + "LibraryConfig.default_address_hrp = \"test\"\n", + "address = Address.new_from_hex(\"0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1\")\n", + "print(address.to_bech32())\n", + "\n", + "# setting back the default value\n", + "LibraryConfig.default_address_hrp = \"erd\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Wallets\n", + "\n", + "#### Generating a mnemonic\n", + "\n", + "Mnemonic generation is based on [`trezor/python-mnemonic`](https://github.com/trezor/python-mnemonic) and can be achieved as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Mnemonic\n", + "\n", + "mnemonic = Mnemonic.generate()\n", + "words = mnemonic.get_words()\n", + "\n", + "print(words)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Saving the mnemonic to a keystore file\n", + "\n", + "The mnemonic can be saved to a keystore file:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Mnemonic, UserWallet\n", + "\n", + "mnemonic = Mnemonic.generate()\n", + "\n", + "# saves the mnemonic to a keystore file with kind=mnemonic\n", + "wallet = UserWallet.from_mnemonic(mnemonic.get_text(), \"password\")\n", + "wallet.save(Path(\"walletWithMnemonic.json\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Deriving secret keys from a mnemonic\n", + "\n", + "Given a mnemonic, we can derive keypairs:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Mnemonic\n", + "\n", + "mnemonic = Mnemonic.generate()\n", + "\n", + "secret_key = mnemonic.derive_key(0)\n", + "public_key = secret_key.generate_public_key()\n", + "\n", + "print(\"Secret key:\", secret_key.hex())\n", + "print(\"Public key:\", public_key.hex())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Saving a secret key to a keystore file\n", + "\n", + "The secret key can also be saved to a keystore file:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Mnemonic, UserWallet\n", + "\n", + "mnemonic = Mnemonic.generate()\n", + "\n", + "# by default, derives using the index = 0\n", + "secret_key = mnemonic.derive_key()\n", + "\n", + "# saves the mnemonic to a keystore file with kind=secretKey\n", + "wallet = UserWallet.from_secret_key(secret_key, \"password\")\n", + "wallet.save(Path(\"walletWithSecretKey.json\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Saving a secrey key to a PEM file\n", + "\n", + "We can save a secret key to a pem file. This is not recommended as it is not secure, but it's very convenient for testing purposes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Address, UserPEM\n", + "\n", + "mnemonic = Mnemonic.generate()\n", + "\n", + "# by default, derives using the index = 0\n", + "secret_key = mnemonic.derive_key()\n", + "\n", + "label = Address(public_key.buffer, \"erd\").to_bech32()\n", + "pem = UserPEM(label=label, secret_key=secret_key)\n", + "pem.save(Path(\"wallet.pem\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Generating a KeyPair\n", + "\n", + "A `KeyPair` is a wrapper over a secret key and a public key. We can create a keypair and use it for signing or verifying. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import KeyPair\n", + "\n", + "keypair = KeyPair.generate()\n", + "\n", + "# get secret key\n", + "secret_key = keypair.get_secret_key()\n", + "\n", + "# get public key\n", + "public_key = keypair.get_public_key()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Loading a wallets from keystore mnemonic file\n", + "\n", + "Load a keystore that holds an _encrypted mnemonic_ (and perform wallet derivation at the same time):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import UserWallet\n", + "\n", + "# loads the mnemonic and derives the a secret key; default index = 0\n", + "secret_key = UserWallet.load_secret_key(Path(\"walletWithMnemonic.json\"), \"password\")\n", + "address = secret_key.generate_public_key().to_address(\"erd\")\n", + "\n", + "print(\"Secret key:\", secret_key.hex())\n", + "print(\"Address:\", address.to_bech32())\n", + "\n", + "# derive secret key with index = 7\n", + "secret_key = UserWallet.load_secret_key(\n", + " path=Path(\"walletWithMnemonic.json\"),\n", + " password=\"password\",\n", + " address_index=7\n", + ")\n", + "address = secret_key.generate_public_key().to_address()\n", + "\n", + "print(\"Secret key:\", secret_key.hex())\n", + "print(\"Address:\", address.to_bech32())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Loading a wallet from a keystore secret key file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import UserWallet\n", + "\n", + "secret_key = UserWallet.load_secret_key(Path(\"walletWithSecretKey.json\"), \"password\")\n", + "address = secret_key.generate_public_key().to_address(\"erd\")\n", + "\n", + "print(\"Secret key:\", secret_key.hex())\n", + "print(\"Address:\", address.to_bech32())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Loading a wallet from a PEM file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import UserPEM\n", + "\n", + "pem = UserPEM.from_file(Path(\"wallet.pem\"))\n", + "\n", + "print(\"Secret key:\", pem.secret_key.hex())\n", + "print(\"Public key:\", pem.public_key.hex())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Signing objects\n", + "\n", + "The signing is performed using the **secret key** of an account. We have a few wrappers over the secret key, like [Account](#creating-accounts) that make siging easier. We'll first learn how we can sign using an `Account` and then we'll see how we can sign using the secret key.\n", + "\n", + "#### Signing a Transaction using an Account\n", + "\n", + "We are going to assume we have an account at this point. If you don't fell free to check out the [creating an account section](#creating-accounts)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, Transaction\n", + "\n", + "account = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "transaction = Transaction(\n", + " nonce=90,\n", + " sender=account.address,\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " value=1000000000000000000,\n", + " gas_limit=50000,\n", + " chain_id=\"D\"\n", + ")\n", + "\n", + "# apply the signature on the transaction\n", + "transaction.signature = account.sign_transaction(transaction)\n", + "\n", + "print(transaction.to_dictionary())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Signing a Transaction using a SecretKey" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Transaction, TransactionComputer, UserSecretKey\n", + "\n", + "secret_key_hex = \"413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9\"\n", + "secret_key = UserSecretKey(bytes.fromhex(secret_key_hex))\n", + "public_key = secret_key.generate_public_key()\n", + "\n", + "transaction = Transaction(\n", + " nonce=90,\n", + " sender=public_key.to_address(),\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " value=1000000000000000000,\n", + " gas_limit=50000,\n", + " chain_id=\"D\"\n", + ")\n", + "\n", + "# serialize the transaction\n", + "transaction_computer = TransactionComputer()\n", + "serialized_transaction = transaction_computer.compute_bytes_for_signing(transaction)\n", + "\n", + "# apply the signature on the transaction\n", + "transaction.signature = secret_key.sign(serialized_transaction)\n", + "\n", + "print(transaction.to_dictionary())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Signing a Transaction by hash" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, Transaction, TransactionComputer\n", + "\n", + "account = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "transaction = Transaction(\n", + " nonce=90,\n", + " sender=account.address,\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " value=1000000000000000000,\n", + " gas_limit=50000,\n", + " chain_id=\"D\"\n", + ")\n", + "\n", + "transaction_computer = TransactionComputer()\n", + "\n", + "# sets the least significant bit of the options field to `1`\n", + "transaction_computer.apply_options_for_hash_signing(transaction)\n", + "\n", + "# compute the hash for signing\n", + "hash = transaction_computer.compute_hash_for_signing(transaction)\n", + "\n", + "# sign and apply the signature on the transaction\n", + "transaction.signature = account.sign(hash)\n", + "\n", + "print(transaction.to_dictionary())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Signing a Message using an Account" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Message, MessageComputer\n", + "\n", + "account = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# creating a message\n", + "message = Message(data=\"this is a test message\".encode(), address=account.address)\n", + "\n", + "# signing the message\n", + "message.signature = account.sign_message(message)\n", + "\n", + "# dictionary representation of the message\n", + "print(MessageComputer().pack_message(message))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Signing a message using a SecretKey" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import UserSecretKey, Message, MessageComputer\n", + "\n", + "secret_key_hex = \"413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9\"\n", + "secret_key = UserSecretKey(bytes.fromhex(secret_key_hex))\n", + "public_key = secret_key.generate_public_key()\n", + "\n", + "message_computer = MessageComputer()\n", + "\n", + "# creating a message\n", + "message = Message(data=\"this is a test message\".encode(), address=public_key.to_address())\n", + "\n", + "# serialize the message\n", + "serialized_message = message_computer.compute_bytes_for_signing(message)\n", + "\n", + "# signing the message\n", + "message.signature = secret_key.sign(serialized_message)\n", + "\n", + "# dictionary representation of the message\n", + "print(message_computer.pack_message(message))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Verifying signatures\n", + "\n", + "The verification of a signature is done using the **public key** of an account. We have a few wrappers over public keys that make the verification of signatures a little bit easier.\n", + "\n", + "#### Verifying a signature using a UserVerifier" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, Transaction, TransactionComputer, UserVerifier\n", + "\n", + "account = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "transaction = Transaction(\n", + " nonce=90,\n", + " sender=account.address,\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " value=1000000000000000000,\n", + " gas_limit=50000,\n", + " chain_id=\"D\"\n", + ")\n", + "\n", + "# apply the signature on the transaction\n", + "transaction.signature = account.sign_transaction(transaction)\n", + "\n", + "# instantiating a user verifier; basically gets the public key\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "alice_verifier = UserVerifier.from_address(alice)\n", + "\n", + "# serialize the transaction for verification\n", + "transaction_computer = TransactionComputer()\n", + "serialized_transaction = transaction_computer.compute_bytes_for_verifying(transaction)\n", + "\n", + "# verify the signature\n", + "is_signed_by_alice = alice_verifier.verify(\n", + " data=serialized_transaction,\n", + " signature=transaction.signature\n", + ")\n", + "\n", + "print(\"Transaction is signed by Alice:\", is_signed_by_alice)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Verifying a signature using the public key" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, Transaction, TransactionComputer, UserPublicKey\n", + "\n", + "account = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "transaction = Transaction(\n", + " nonce=90,\n", + " sender=account.address,\n", + " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", + " value=1000000000000000000,\n", + " gas_limit=50000,\n", + " chain_id=\"D\"\n", + ")\n", + "\n", + "# apply the signature on the transaction\n", + "transaction.signature = account.sign_transaction(transaction)\n", + "\n", + "# instantiating a public key\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "public_key = UserPublicKey(alice.get_public_key())\n", + "\n", + "# serialize the transaction for verification\n", + "transaction_computer = TransactionComputer()\n", + "serialized_transaction = transaction_computer.compute_bytes_for_verifying(transaction)\n", + "\n", + "# verify the signature\n", + "is_signed_by_alice = public_key.verify(\n", + " data=serialized_transaction,\n", + " signature=transaction.signature\n", + ")\n", + "\n", + "print(\"Transaction is signed by Alice:\", is_signed_by_alice)" ] } ], diff --git a/multiversx_sdk/account_management/account_controller.py b/multiversx_sdk/account_management/account_controller.py index fc023544..655eaa05 100644 --- a/multiversx_sdk/account_management/account_controller.py +++ b/multiversx_sdk/account_management/account_controller.py @@ -66,13 +66,13 @@ def create_transaction_for_guarding_account(self, def create_transaction_for_unguarding_account(self, sender: IAccount, nonce: int, - guardian: Optional[Address] = None, + guardian: Address, relayer: Optional[Address] = None) -> Transaction: transaction = self.factory.create_transaction_for_unguarding_account( - sender=sender.address + sender=sender.address, + guardian=guardian ) - transaction.guardian = guardian transaction.relayer = relayer transaction.nonce = nonce transaction.signature = sender.sign(self.tx_computer.compute_bytes_for_signing(transaction)) diff --git a/multiversx_sdk/account_management/account_transactions_factory.py b/multiversx_sdk/account_management/account_transactions_factory.py index 472d60c3..be578edc 100644 --- a/multiversx_sdk/account_management/account_transactions_factory.py +++ b/multiversx_sdk/account_management/account_transactions_factory.py @@ -59,7 +59,7 @@ def create_transaction_for_guarding_account(self, sender: Address) -> Transactio add_data_movement_gas=True ).build() - def create_transaction_for_unguarding_account(self, sender: Address) -> Transaction: + def create_transaction_for_unguarding_account(self, sender: Address, guardian: Address) -> Transaction: data_parts = ["UnGuardAccount"] transaction = TransactionBuilder( @@ -71,6 +71,7 @@ def create_transaction_for_unguarding_account(self, sender: Address) -> Transact add_data_movement_gas=True ).build() transaction.options = 2 + transaction.guardian = guardian return transaction diff --git a/multiversx_sdk/entrypoints/entrypoints.py b/multiversx_sdk/entrypoints/entrypoints.py index 8cc8c0c8..8275c739 100644 --- a/multiversx_sdk/entrypoints/entrypoints.py +++ b/multiversx_sdk/entrypoints/entrypoints.py @@ -102,6 +102,9 @@ def send_transaction(self, transaction: Transaction) -> bytes: def await_completed_transaction(self, tx_hash: Union[str, bytes]) -> TransactionOnNetwork: return self.network_provider.await_transaction_completed(tx_hash) + def get_transaction(self, tx_hash: Union[str, bytes]) -> TransactionOnNetwork: + return self.network_provider.get_transaction(tx_hash) + def create_network_provider(self) -> Union[ApiNetworkProvider, ProxyNetworkProvider]: return self.network_provider From cebda52a86322f4bc3577a43889e8623fbd51982 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 10 Dec 2024 18:13:55 +0200 Subject: [PATCH 07/17] rename entrypoint method to match network provider --- multiversx_sdk/entrypoints/entrypoints.py | 2 +- multiversx_sdk/entrypoints/entrypoints_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk/entrypoints/entrypoints.py b/multiversx_sdk/entrypoints/entrypoints.py index 8275c739..b9837c2d 100644 --- a/multiversx_sdk/entrypoints/entrypoints.py +++ b/multiversx_sdk/entrypoints/entrypoints.py @@ -99,7 +99,7 @@ def send_transactions(self, transactions: list[Transaction]) -> tuple[int, list[ def send_transaction(self, transaction: Transaction) -> bytes: return self.network_provider.send_transaction(transaction) - def await_completed_transaction(self, tx_hash: Union[str, bytes]) -> TransactionOnNetwork: + def await_transaction_completed(self, tx_hash: Union[str, bytes]) -> TransactionOnNetwork: return self.network_provider.await_transaction_completed(tx_hash) def get_transaction(self, tx_hash: Union[str, bytes]) -> TransactionOnNetwork: diff --git a/multiversx_sdk/entrypoints/entrypoints_test.py b/multiversx_sdk/entrypoints/entrypoints_test.py index 2b6697dd..5e62af59 100644 --- a/multiversx_sdk/entrypoints/entrypoints_test.py +++ b/multiversx_sdk/entrypoints/entrypoints_test.py @@ -85,7 +85,7 @@ def test_contract_flow(self): ) tx_hash = self.entrypoint.send_transaction(transaction) - self.entrypoint.await_completed_transaction(tx_hash) + self.entrypoint.await_transaction_completed(tx_hash) query_result = controller.query( contract=contract_address, From 416390d6465fd71772ba2b11c3c41d9680418b8a Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 10 Dec 2024 20:13:49 +0200 Subject: [PATCH 08/17] add starting a project section --- examples/v1.ipynb | 159 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 140 insertions(+), 19 deletions(-) diff --git a/examples/v1.ipynb b/examples/v1.ipynb index 94714fbd..7b134df0 100644 --- a/examples/v1.ipynb +++ b/examples/v1.ipynb @@ -295,7 +295,7 @@ "- `send_transaction(transaction: Transaction) -> bytes;`\n", "- `send_transactions(transactions: list[Transaction]) -> tuple[int, list[bytes]];`\n", "- `get_transaction(tx_hash: str | bytes) -> TransactionOnNetwork;`\n", - "- `await_completed_transaction(tx_hash: str | bytes) -> TransactionOnNetwork;`\n", + "- `await_transaction_completed(tx_hash: str | bytes) -> TransactionOnNetwork;`\n", "\n", "Some other methods are exposed through a so called network provider. There are two types of network providers: ApiNetworkProvider and ProxyNetworkProvider. The ProxyNetworkProvider interacts directly with the proxy of an observing squad. The ApiNetworkProvider, as the name suggests, interacts with the api, that is a layer over the proxy. It fetches data from the network but also from Elastic Search." ] @@ -1452,7 +1452,7 @@ "source": [ "# we use the transaction hash we got when broadcasting the transaction\n", "# waiting for transaction completion\n", - "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "transaction_on_network = entrypoint.await_transaction_completed(tx_hash)\n", "\n", "# parsing the transaction\n", "contract_deploy_outcome = controller.parse_deploy(transaction_on_network)" @@ -1556,7 +1556,7 @@ "print(tx_hash.hex())\n", "\n", "# waiting for transaction to complete\n", - "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "transaction_on_network = entrypoint.await_transaction_completed(tx_hash)\n", "\n", "# parsing transaction\n", "parser = SmartContractTransactionsOutcomeParser(abi)\n", @@ -2170,7 +2170,7 @@ "tx_hash = entrypoint.send_transaction(transaction)\n", "\n", "# if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)`\n", - "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "transaction_on_network = entrypoint.await_transaction_completed(tx_hash)\n", "\n", "# extract the token identifier\n", "parser = TokenManagementTransactionsOutcomeParser()\n", @@ -2270,7 +2270,7 @@ "tx_hash = entrypoint.send_transaction(transaction)\n", "\n", "# waits until the transaction is processed and fetches it from the network\n", - "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "transaction_on_network = entrypoint.await_transaction_completed(tx_hash)\n", "\n", "# extract the roles\n", "parser = TokenManagementTransactionsOutcomeParser()\n", @@ -2376,7 +2376,7 @@ "tx_hash = entrypoint.send_transaction(transaction)\n", "\n", "# waits until the transaction is processed and fetches it from the network\n", - "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "transaction_on_network = entrypoint.await_transaction_completed(tx_hash)\n", "\n", "# extract the token identifier\n", "parser = TokenManagementTransactionsOutcomeParser()\n", @@ -2506,7 +2506,7 @@ "tx_hash = entrypoint.send_transaction(transaction)\n", "\n", "# if we know that the transaction is completed, we can simply call `get_transaction(tx_hash)`\n", - "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "transaction_on_network = entrypoint.await_transaction_completed(tx_hash)\n", "\n", "# extract the collection identifier\n", "parser = TokenManagementTransactionsOutcomeParser()\n", @@ -2535,7 +2535,7 @@ "tx_hash = entrypoint.send_transaction(transaction)\n", "\n", "# waits until the transaction is processed and fetches it from the network\n", - "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "transaction_on_network = entrypoint.await_transaction_completed(tx_hash)\n", "\n", "# extract the nft identifier\n", "parser = TokenManagementTransactionsOutcomeParser()\n", @@ -2985,7 +2985,7 @@ "tx_hash = entrypoint.send_transaction(transaction)\n", "\n", "# waits until the transaction is processed and fetches it from the network\n", - "transaction_on_network = entrypoint.await_completed_transaction(tx_hash)\n", + "transaction_on_network = entrypoint.await_transaction_completed(tx_hash)\n", "\n", "# extract the contract's address\n", "parser = DelegationTransactionsOutcomeParser()\n", @@ -4194,7 +4194,7 @@ "# sets the least significant bit of the options field to `1`\n", "transaction_computer.apply_options_for_hash_signing(transaction)\n", "\n", - "# compute the hash for signing\n", + "# compute a keccak256 hash for signing\n", "hash = transaction_computer.compute_hash_for_signing(transaction)\n", "\n", "# sign and apply the signature on the transaction\n", @@ -4217,7 +4217,7 @@ "outputs": [], "source": [ "from pathlib import Path\n", - "from multiversx_sdk import Account, Message, MessageComputer\n", + "from multiversx_sdk import Account, Message\n", "\n", "account = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", "\n", @@ -4225,10 +4225,7 @@ "message = Message(data=\"this is a test message\".encode(), address=account.address)\n", "\n", "# signing the message\n", - "message.signature = account.sign_message(message)\n", - "\n", - "# dictionary representation of the message\n", - "print(MessageComputer().pack_message(message))" + "message.signature = account.sign_message(message)" ] }, { @@ -4259,10 +4256,7 @@ "serialized_message = message_computer.compute_bytes_for_signing(message)\n", "\n", "# signing the message\n", - "message.signature = secret_key.sign(serialized_message)\n", - "\n", - "# dictionary representation of the message\n", - "print(message_computer.pack_message(message))" + "message.signature = secret_key.sign(serialized_message)" ] }, { @@ -4362,6 +4356,133 @@ "\n", "print(\"Transaction is signed by Alice:\", is_signed_by_alice)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sending messages over boundaries\n", + "\n", + "Generally speaking, signed `Message` objects are meant to be sent to a remote party (e.g. a service), which can then verify the signature.\n", + "\n", + "In order to prepare a message for transmission, you can use the `MessageComputer.packMessage()` utility method:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Message, MessageComputer\n", + "\n", + "account = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "# creating a message\n", + "message = Message(data=\"this is a test message\".encode(), address=account.address)\n", + "\n", + "# signing the message\n", + "message.signature = account.sign_message(message)\n", + "\n", + "message_computer = MessageComputer()\n", + "packed_message = message_computer.pack_message(message)\n", + "print(packed_message)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then, on the receiving side, you can use `MessageComputer.unpackMessage()` to reconstruct the message, prior verification:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import Address, MessageComputer, UserPublicKey\n", + "\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "\n", + "message_computer = MessageComputer()\n", + "\n", + "# restore message\n", + "message = message_computer.unpack_message(packed_message)\n", + "\n", + "# verify signature\n", + "public_key = UserPublicKey(alice.get_public_key())\n", + "\n", + "is_signed_by_alice = public_key.verify(message_computer.compute_bytes_for_verifying(message), message.signature)\n", + "print(\"Is signed by Alice:\", is_signed_by_alice)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Start your first project\n", + "\n", + "We recommend using a Python version equal to `3.11` or higher, but the sdk should work with any version higher than `3.8`. We also recommend using a virtual environment for managing dependencies. Make sure you also have `pip` installed on your machine.\n", + "\n", + "Using a Terminal or Console, create a directory on your system (hello-multiversx in this example) and make it the working directory.\n", + "\n", + "```sh\n", + "mkdir hello-multiversx\n", + "cd hello-multiversx\n", + "```\n", + "\n", + "### Create a virtual environment\n", + "\n", + "Run the following command to create and activate your virtual environment:\n", + "\n", + "```sh\n", + "python3 -m venv ./venv\n", + "source ./venv/bin/activate\n", + "```\n", + "\n", + "After the virtual environment is created, we can install the sdk running the following command:\n", + "\n", + "```sh\n", + "pip install multiversx-sdk\n", + "```\n", + "\n", + "If your project has multiple dependencies, we recommend using a `requirements.txt` file for having all dependencies in one place. Inside the file we are going to place each dependency on a new line:\n", + "\n", + "```sh\n", + "multiversx-sdk\n", + "```\n", + "\n", + "Aditionally, we can also install it directly from GitHub. Place this line on a new line of your `requirements.txt` file. In this example, we are going to install the version `1.0.0`:\n", + "\n", + "```sh\n", + "git+https://git@github.com/multiversx/mx-sdk-py.git@v1.0.0#egg=multiversx_sdk\n", + "```\n", + "\n", + "If you've places all dependencies in a `requirements.txt` file, make sure you also install them by running:\n", + "\n", + "```sh\n", + "pip install -r requirements.txt\n", + "```\n", + "\n", + "We can then create a `main.py` file where we can write our code.\n", + "\n", + "### Importing objects from the sdk\n", + "\n", + "The most common classes can be imported from package level:\n", + "\n", + "```py\n", + "from multiversx_sdk import Address, Transaction\n", + "```\n", + "\n", + "When interacting with smart contracts, we might want to make use of the abi file or other contract types. We should import those from the abi subpackage.\n", + "\n", + "```py\n", + "from multiversx_sdk.abi import Abi, BigUIntValue, StringValue\n", + "```" + ] } ], "metadata": { From 85f0ff739b543d3dcd55f01062098a76c5b02850 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 11 Dec 2024 09:05:33 +0200 Subject: [PATCH 09/17] fix test --- .../account_management/account_transactions_factory_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/multiversx_sdk/account_management/account_transactions_factory_test.py b/multiversx_sdk/account_management/account_transactions_factory_test.py index 13de1f25..23e185db 100644 --- a/multiversx_sdk/account_management/account_transactions_factory_test.py +++ b/multiversx_sdk/account_management/account_transactions_factory_test.py @@ -53,9 +53,11 @@ def test_guard_account(self): def test_unguard_account(self): sender = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") - tx = self.factory.create_transaction_for_unguarding_account(sender) + guardian = Address.new_from_bech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx") + tx = self.factory.create_transaction_for_unguarding_account(sender, guardian) assert tx.sender.to_bech32() == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert tx.receiver.to_bech32() == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert tx.data.decode() == "UnGuardAccount" assert tx.gas_limit == 321000 + assert tx.guardian == guardian From 38c3a443fbffe0dade61fdce9e2dce2299c18172 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 11 Dec 2024 12:08:11 +0200 Subject: [PATCH 10/17] fixes --- examples/v1.ipynb | 31 ++++++++++--------- multiversx_sdk/accounts/account.py | 2 +- .../network_providers/api_network_provider.py | 18 ++++++++--- .../api_network_provider_test.py | 23 ++++++++++---- .../network_providers/http_resources.py | 2 +- .../proxy_network_provider.py | 17 +++++++--- .../proxy_network_provider_test.py | 23 ++++++++++---- 7 files changed, 78 insertions(+), 38 deletions(-) diff --git a/examples/v1.ipynb b/examples/v1.ipynb index 7b134df0..73fd2d46 100644 --- a/examples/v1.ipynb +++ b/examples/v1.ipynb @@ -425,8 +425,7 @@ "entrypoint = DevnetEntrypoint()\n", "api = entrypoint.create_network_provider()\n", "\n", - "args = GetBlockArguments(block_hash=bytes.fromhex(\n", - " \"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"))\n", + "args = GetBlockArguments(block_hash=bytes.fromhex(\"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"))\n", "block = api.get_block(arguments=args)" ] }, @@ -674,8 +673,7 @@ " chain_id=\"D\",\n", " data=b\"add@07\"\n", ")\n", - "\n", - "transaction_hash = api.simulate_transaction(transaction)" + "transaction_on_network = api.simulate_transaction(transaction)" ] }, { @@ -701,15 +699,18 @@ "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", "contract = Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn\")\n", "\n", + "nonce = entrypoint.recall_account_nonce(alice)\n", + "\n", "transaction = Transaction(\n", " sender=alice,\n", " receiver=contract,\n", " gas_limit=5000000,\n", " chain_id=\"D\",\n", - " data=b\"add@07\"\n", + " data=b\"add@07\",\n", + " nonce=nonce\n", ")\n", "\n", - "transaction_hash = api.estimate_transaction_cost(transaction)" + "transaction_cost_response = api.estimate_transaction_cost(transaction)" ] }, { @@ -812,6 +813,7 @@ "\n", "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", "\n", + "# these tokens are just for the example, they do not belong to Alice\n", "token = Token(identifier=\"TEST-123456\") # ESDT\n", "token_on_network = api.get_token_of_account(address=alice, token=token)\n", "\n", @@ -825,7 +827,7 @@ "source": [ "### Fetching all fungible tokens of an account\n", "\n", - "Fetches all fungible tokens held by an account. This method does not handle pagination, but can be achieved by using `do_get_generic`." + "Fetches all fungible tokens held by an account. This method does not handle pagination, that can be achieved by using `do_get_generic`." ] }, { @@ -840,7 +842,7 @@ "api = entrypoint.create_network_provider()\n", "\n", "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", - "fungible_token = api.get_fungible_tokens_of_account(address=alice)" + "fungible_tokens = api.get_fungible_tokens_of_account(address=alice)" ] }, { @@ -864,7 +866,7 @@ "api = entrypoint.create_network_provider()\n", "\n", "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", - "fungible_token = api.get_non_fungible_tokens_of_account(address=alice)" + "nfts = api.get_non_fungible_tokens_of_account(address=alice)" ] }, { @@ -916,8 +918,8 @@ "api = entrypoint.create_network_provider()\n", "\n", "query = SmartContractQuery(\n", - " contract=Address.new_from_bech32(\"erd1qqqqq...\"),\n", - " function=\"testFunction\",\n", + " contract=Address.new_from_bech32(\"erd1qqqqqqqqqqqqqpgq076flgeualrdu5jyyj60snvrh7zu4qrg05vqez5jen\"),\n", + " function=\"getSum\",\n", " arguments=[]\n", ")\n", "response = api.query_contract(query=query)" @@ -1140,7 +1142,9 @@ " nonce=alice.get_nonce_then_increment(),\n", " receiver=Address.new_from_bech32(\"erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx\"),\n", " token_transfers=[first_transfer, second_transfer, third_transfer]\n", - ")" + ")\n", + "\n", + "tx_hash = entrypoint.send_transaction(transaction)" ] }, { @@ -1400,8 +1404,7 @@ ")\n", "\n", "# broadcasting the transaction\n", - "tx_hash = entrypoint.send_transaction(deploy_transaction)\n", - "print(tx_hash.hex())" + "tx_hash = entrypoint.send_transaction(deploy_transaction)" ] }, { diff --git a/multiversx_sdk/accounts/account.py b/multiversx_sdk/accounts/account.py index 62bd44ba..46b16641 100644 --- a/multiversx_sdk/accounts/account.py +++ b/multiversx_sdk/accounts/account.py @@ -28,7 +28,7 @@ def new_from_pem(cls, file_path: Path, index: int = 0, hrp: Optional[str] = None def new_from_keystore(cls, file_path: Path, password: str, - address_index: int = 0, + address_index: Optional[int] = None, hrp: Optional[str] = None) -> "Account": secret_key = UserWallet.load_secret_key(file_path, password, address_index) return Account(secret_key, hrp) diff --git a/multiversx_sdk/network_providers/api_network_provider.py b/multiversx_sdk/network_providers/api_network_provider.py index 3eb102bb..fe2b1c04 100644 --- a/multiversx_sdk/network_providers/api_network_provider.py +++ b/multiversx_sdk/network_providers/api_network_provider.py @@ -61,11 +61,11 @@ def get_network_status(self, shard: int = METACHAIN_ID) -> NetworkStatus: return self.backing_proxy.get_network_status(shard) def get_block(self, arguments: GetBlockArguments) -> BlockOnNetwork: - """Fetches a block by nonce or by hash.""" + """Fetches a block by hash.""" if not arguments.block_hash: raise Exception("Block hash not provided. Please set the `block_hash` in the arguments.") - result = self.do_get_generic(f"blocks/{arguments.block_hash.decode()}") + result = self.do_get_generic(f"blocks/{arguments.block_hash.hex()}") return block_from_response(result) def get_latest_block(self, shard: Optional[int] = None) -> BlockOnNetwork: @@ -80,7 +80,10 @@ def get_account(self, address: Address) -> AccountOnNetwork: return account def get_account_storage(self, address: Address) -> AccountStorage: - """Fetches the storage (key-value pairs) of an account.""" + """ + Fetches the storage (key-value pairs) of an account. + When decoding the keys, the errors are ignored. Use the raw values if needed. + """ response: dict[str, Any] = self.do_get_generic(f"address/{address.to_bech32()}/keys") return account_storage_from_response(response.get("data", {})) @@ -112,9 +115,14 @@ def send_transaction(self, transaction: Transaction) -> bytes: response = self.do_post_generic("transactions", transaction.to_dictionary()) return bytes.fromhex(response.get('txHash', '')) - def simulate_transaction(self, transaction: Transaction) -> TransactionOnNetwork: + def simulate_transaction(self, transaction: Transaction, check_signature: bool = False) -> TransactionOnNetwork: """Simulates a transaction.""" - response: dict[str, Any] = self.do_post_generic('transaction/simulate', transaction.to_dictionary()) + url = 'transaction/simulate?checkSignature=false' + + if check_signature: + url = 'transaction/simulate' + + response: dict[str, Any] = self.do_post_generic(url, transaction.to_dictionary()) return transaction_from_simulate_response(transaction, response.get("data", {}).get("result", {})) def estimate_transaction_cost(self, transaction: Transaction) -> TransactionCostResponse: diff --git a/multiversx_sdk/network_providers/api_network_provider_test.py b/multiversx_sdk/network_providers/api_network_provider_test.py index 2548ffbe..340fda4d 100644 --- a/multiversx_sdk/network_providers/api_network_provider_test.py +++ b/multiversx_sdk/network_providers/api_network_provider_test.py @@ -47,7 +47,7 @@ def test_get_block(self): self.api.get_block(args) args = GetBlockArguments( - block_hash="ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f".encode() + block_hash=bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f") ) result_by_hash = self.api.get_block(args) @@ -192,12 +192,14 @@ def test_simulate_transaction(self): chain_id="D", ) nonce = self.api.get_account(Address.new_from_bech32(bob.label)).nonce - transaction.nonce = nonce - transaction.signature = bob.secret_key.sign(tx_computer.compute_bytes_for_signing(transaction)) tx_on_network = self.api.simulate_transaction(transaction) + assert tx_on_network.status == TransactionStatus("success") + + transaction.signature = bob.secret_key.sign(tx_computer.compute_bytes_for_signing(transaction)) + tx_on_network = self.api.simulate_transaction(transaction=transaction, check_signature=True) assert tx_on_network.status == TransactionStatus("success") transaction = Transaction( @@ -205,12 +207,21 @@ def test_simulate_transaction(self): receiver=Address.new_from_bech32("erd1qqqqqqqqqqqqqpgq076flgeualrdu5jyyj60snvrh7zu4qrg05vqez5jen"), gas_limit=10000000, chain_id="D", - data=b"add@07" + data=b"add@07", + nonce=nonce ) - transaction.nonce = nonce + tx_on_network = self.api.simulate_transaction(transaction) + + assert tx_on_network.status == TransactionStatus("success") + assert len(tx_on_network.smart_contract_results) == 1 + assert tx_on_network.smart_contract_results[0].sender.to_bech32( + ) == "erd1qqqqqqqqqqqqqpgq076flgeualrdu5jyyj60snvrh7zu4qrg05vqez5jen" + assert tx_on_network.smart_contract_results[0].receiver.to_bech32() == bob.label + assert tx_on_network.smart_contract_results[0].data == b"@6f6b" + transaction.signature = bob.secret_key.sign(tx_computer.compute_bytes_for_signing(transaction)) - tx_on_network = self.api.simulate_transaction(transaction) + tx_on_network = self.api.simulate_transaction(transaction=transaction, check_signature=True) assert tx_on_network.status == TransactionStatus("success") assert len(tx_on_network.smart_contract_results) == 1 diff --git a/multiversx_sdk/network_providers/http_resources.py b/multiversx_sdk/network_providers/http_resources.py index 6e324d2a..3d620b0e 100644 --- a/multiversx_sdk/network_providers/http_resources.py +++ b/multiversx_sdk/network_providers/http_resources.py @@ -454,7 +454,7 @@ def account_storage_from_response(raw_response: dict[str, Any]) -> AccountStorag entries.append( AccountStorageEntry( raw={key: value}, - key=decoded_key.decode(), + key=decoded_key.decode(errors="ignore"), value=decoded_value ) ) diff --git a/multiversx_sdk/network_providers/proxy_network_provider.py b/multiversx_sdk/network_providers/proxy_network_provider.py index 43d796b8..7bbbe4c1 100644 --- a/multiversx_sdk/network_providers/proxy_network_provider.py +++ b/multiversx_sdk/network_providers/proxy_network_provider.py @@ -74,7 +74,7 @@ def get_block(self, arguments: GetBlockArguments) -> BlockOnNetwork: raise Exception("Shard not provided. Please set the shard in the arguments.") if arguments.block_hash: - response = self.do_get_generic(f"block/{arguments.shard}/by-hash/{arguments.block_hash.decode()}") + response = self.do_get_generic(f"block/{arguments.shard}/by-hash/{arguments.block_hash.hex()}") elif arguments.block_nonce: response = self.do_get_generic(f"block/{arguments.shard}/by-nonce/{arguments.block_nonce}") else: @@ -108,7 +108,10 @@ def _get_guardian_data(self, address: Address, return_data: dict[str, bool]): return_data["is_guarded"] = bool(guardian_data.get("guardianData", {}).get("guarded")) def get_account_storage(self, address: Address) -> AccountStorage: - """Fetches the storage (key-value pairs) of an account.""" + """ + Fetches the storage (key-value pairs) of an account. + When decoding the keys, the errors are ignored. Use the raw values if needed. + """ response = self.do_get_generic(f"address/{address.to_bech32()}/keys") return account_storage_from_response(response.to_dictionary()) @@ -141,10 +144,14 @@ def send_transaction(self, transaction: Transaction) -> bytes: 'transaction/send', transaction.to_dictionary()) return bytes.fromhex(response.get('txHash', '')) - def simulate_transaction(self, transaction: Transaction) -> TransactionOnNetwork: + def simulate_transaction(self, transaction: Transaction, check_signature: bool = False) -> TransactionOnNetwork: """Simulates a transaction.""" - response = self.do_post_generic( - 'transaction/simulate', transaction.to_dictionary()) + url = 'transaction/simulate?checkSignature=false' + + if check_signature: + url = 'transaction/simulate' + + response = self.do_post_generic(url, transaction.to_dictionary()) return transaction_from_simulate_response(transaction, response.to_dictionary().get("result", {})) def estimate_transaction_cost(self, transaction: Transaction) -> TransactionCostResponse: diff --git a/multiversx_sdk/network_providers/proxy_network_provider_test.py b/multiversx_sdk/network_providers/proxy_network_provider_test.py index ac789793..8e95f8ea 100644 --- a/multiversx_sdk/network_providers/proxy_network_provider_test.py +++ b/multiversx_sdk/network_providers/proxy_network_provider_test.py @@ -46,7 +46,7 @@ def test_get_block(self): self.proxy.get_block(args) args = GetBlockArguments( - shard=1, block_hash="ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f".encode() + shard=1, block_hash=bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f") ) result_by_hash = self.proxy.get_block(args) @@ -301,12 +301,14 @@ def test_simulate_transaction(self): chain_id="D", ) nonce = self.proxy.get_account(Address.new_from_bech32(bob.label)).nonce - transaction.nonce = nonce - transaction.signature = bob.secret_key.sign(tx_computer.compute_bytes_for_signing(transaction)) tx_on_network = self.proxy.simulate_transaction(transaction) + assert tx_on_network.status == TransactionStatus("success") + + transaction.signature = bob.secret_key.sign(tx_computer.compute_bytes_for_signing(transaction)) + tx_on_network = self.proxy.simulate_transaction(transaction=transaction, check_signature=True) assert tx_on_network.status == TransactionStatus("success") transaction = Transaction( @@ -314,12 +316,21 @@ def test_simulate_transaction(self): receiver=Address.new_from_bech32("erd1qqqqqqqqqqqqqpgq076flgeualrdu5jyyj60snvrh7zu4qrg05vqez5jen"), gas_limit=10000000, chain_id="D", - data=b"add@07" + data=b"add@07", + nonce=nonce ) - transaction.nonce = nonce + tx_on_network = self.proxy.simulate_transaction(transaction) + + assert tx_on_network.status == TransactionStatus("success") + assert len(tx_on_network.smart_contract_results) == 1 + assert tx_on_network.smart_contract_results[0].sender.to_bech32( + ) == "erd1qqqqqqqqqqqqqpgq076flgeualrdu5jyyj60snvrh7zu4qrg05vqez5jen" + assert tx_on_network.smart_contract_results[0].receiver.to_bech32() == bob.label + assert tx_on_network.smart_contract_results[0].data == b"@6f6b" + transaction.signature = bob.secret_key.sign(tx_computer.compute_bytes_for_signing(transaction)) - tx_on_network = self.proxy.simulate_transaction(transaction) + tx_on_network = self.proxy.simulate_transaction(transaction=transaction, check_signature=True) assert tx_on_network.status == TransactionStatus("success") assert len(tx_on_network.smart_contract_results) == 1 From 9d1d31c08012d843428ef9f8cb54b60f0984304f Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 12 Dec 2024 14:44:41 +0200 Subject: [PATCH 11/17] fix tests --- multiversx_sdk/network_providers/api_network_provider_test.py | 4 +++- .../network_providers/proxy_network_provider_test.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk/network_providers/api_network_provider_test.py b/multiversx_sdk/network_providers/api_network_provider_test.py index 340fda4d..8403f186 100644 --- a/multiversx_sdk/network_providers/api_network_provider_test.py +++ b/multiversx_sdk/network_providers/api_network_provider_test.py @@ -190,6 +190,7 @@ def test_simulate_transaction(self): receiver=Address.new_from_bech32(bob.label), gas_limit=50000, chain_id="D", + signature=bytes.fromhex("".join(["0"] * 128)) ) nonce = self.api.get_account(Address.new_from_bech32(bob.label)).nonce transaction.nonce = nonce @@ -208,7 +209,8 @@ def test_simulate_transaction(self): gas_limit=10000000, chain_id="D", data=b"add@07", - nonce=nonce + nonce=nonce, + signature=bytes.fromhex("".join(["0"] * 128)) ) tx_on_network = self.api.simulate_transaction(transaction) diff --git a/multiversx_sdk/network_providers/proxy_network_provider_test.py b/multiversx_sdk/network_providers/proxy_network_provider_test.py index 8e95f8ea..17169eb6 100644 --- a/multiversx_sdk/network_providers/proxy_network_provider_test.py +++ b/multiversx_sdk/network_providers/proxy_network_provider_test.py @@ -299,6 +299,7 @@ def test_simulate_transaction(self): receiver=Address.new_from_bech32(bob.label), gas_limit=50000, chain_id="D", + signature=bytes.fromhex("".join(["0"] * 128)) ) nonce = self.proxy.get_account(Address.new_from_bech32(bob.label)).nonce transaction.nonce = nonce @@ -317,7 +318,8 @@ def test_simulate_transaction(self): gas_limit=10000000, chain_id="D", data=b"add@07", - nonce=nonce + nonce=nonce, + signature=bytes.fromhex("".join(["0"] * 128)) ) tx_on_network = self.proxy.simulate_transaction(transaction) From 37512fd4b1d2eeea094468f0e4fbc867998de88f Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 12 Dec 2024 15:11:05 +0200 Subject: [PATCH 12/17] re-generate docs --- README.md | 2 +- docs/multiversx_sdk.account_management.rst | 29 ++++++++++ docs/multiversx_sdk.builders.rst | 29 ++++++++++ docs/multiversx_sdk.core.rst | 40 +++++++------- docs/multiversx_sdk.delegation.rst | 53 +++++++++++++++++++ docs/multiversx_sdk.network_providers.rst | 8 +++ docs/multiversx_sdk.relayed.rst | 37 +++++++++++++ docs/multiversx_sdk.rst | 8 ++- docs/multiversx_sdk.smart_contracts.rst | 61 ++++++++++++++++++++++ docs/multiversx_sdk.token_management.rst | 45 ++++++++++++++++ docs/multiversx_sdk.transfers.rst | 29 ++++++++++ docs/multiversx_sdk.wallet.rst | 8 +++ 12 files changed, 326 insertions(+), 23 deletions(-) create mode 100644 docs/multiversx_sdk.account_management.rst create mode 100644 docs/multiversx_sdk.builders.rst create mode 100644 docs/multiversx_sdk.delegation.rst create mode 100644 docs/multiversx_sdk.relayed.rst create mode 100644 docs/multiversx_sdk.smart_contracts.rst create mode 100644 docs/multiversx_sdk.token_management.rst create mode 100644 docs/multiversx_sdk.transfers.rst diff --git a/README.md b/README.md index 06488f89..fad09575 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ coverage html Each time a new module/submodule is added it needs to be added to the docs, as well. To do so `cd` in the root directory then run the following command: ```bash -sphinx-apidoc -f -o docs/ multiversx_sdk/ *_test.py *constants.py +sphinx-apidoc -f -o docs/ multiversx_sdk/ '*_test.py' '*constants.py' ``` This command will regenerate the `.rst` files for each module, excluding the tests and the `constants.py` files. diff --git a/docs/multiversx_sdk.account_management.rst b/docs/multiversx_sdk.account_management.rst new file mode 100644 index 00000000..2de09c3e --- /dev/null +++ b/docs/multiversx_sdk.account_management.rst @@ -0,0 +1,29 @@ +multiversx\_sdk.account\_management package +=========================================== + +Submodules +---------- + +multiversx\_sdk.account\_management.account\_controller module +-------------------------------------------------------------- + +.. automodule:: multiversx_sdk.account_management.account_controller + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.account\_management.account\_transactions\_factory module +------------------------------------------------------------------------- + +.. automodule:: multiversx_sdk.account_management.account_transactions_factory + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: multiversx_sdk.account_management + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/multiversx_sdk.builders.rst b/docs/multiversx_sdk.builders.rst new file mode 100644 index 00000000..46eaba7d --- /dev/null +++ b/docs/multiversx_sdk.builders.rst @@ -0,0 +1,29 @@ +multiversx\_sdk.builders package +================================ + +Submodules +---------- + +multiversx\_sdk.builders.token\_transfers\_data\_builder module +--------------------------------------------------------------- + +.. automodule:: multiversx_sdk.builders.token_transfers_data_builder + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.builders.transaction\_builder module +---------------------------------------------------- + +.. automodule:: multiversx_sdk.builders.transaction_builder + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: multiversx_sdk.builders + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/multiversx_sdk.core.rst b/docs/multiversx_sdk.core.rst index 3a38208f..77d49359 100644 --- a/docs/multiversx_sdk.core.rst +++ b/docs/multiversx_sdk.core.rst @@ -8,8 +8,6 @@ Subpackages :maxdepth: 4 multiversx_sdk.core.proto - multiversx_sdk.core.transactions_factories - multiversx_sdk.core.transactions_outcome_parsers Submodules ---------- @@ -38,6 +36,14 @@ multiversx\_sdk.core.code\_metadata module :undoc-members: :show-inheritance: +multiversx\_sdk.core.config module +---------------------------------- + +.. automodule:: multiversx_sdk.core.config + :members: + :undoc-members: + :show-inheritance: + multiversx\_sdk.core.errors module ---------------------------------- @@ -62,22 +68,6 @@ multiversx\_sdk.core.message module :undoc-members: :show-inheritance: -multiversx\_sdk.core.smart\_contract\_queries\_controller module ----------------------------------------------------------------- - -.. automodule:: multiversx_sdk.core.smart_contract_queries_controller - :members: - :undoc-members: - :show-inheritance: - -multiversx\_sdk.core.smart\_contract\_query module --------------------------------------------------- - -.. automodule:: multiversx_sdk.core.smart_contract_query - :members: - :undoc-members: - :show-inheritance: - multiversx\_sdk.core.tokens module ---------------------------------- @@ -102,6 +92,14 @@ multiversx\_sdk.core.transaction\_computer module :undoc-members: :show-inheritance: +multiversx\_sdk.core.transaction\_events\_parser module +------------------------------------------------------- + +.. automodule:: multiversx_sdk.core.transaction_events_parser + :members: + :undoc-members: + :show-inheritance: + multiversx\_sdk.core.transaction\_on\_network module ---------------------------------------------------- @@ -118,10 +116,10 @@ multiversx\_sdk.core.transaction\_status module :undoc-members: :show-inheritance: -multiversx\_sdk.core.typecheck module -------------------------------------- +multiversx\_sdk.core.transactions\_factory\_config module +--------------------------------------------------------- -.. automodule:: multiversx_sdk.core.typecheck +.. automodule:: multiversx_sdk.core.transactions_factory_config :members: :undoc-members: :show-inheritance: diff --git a/docs/multiversx_sdk.delegation.rst b/docs/multiversx_sdk.delegation.rst new file mode 100644 index 00000000..15e61d56 --- /dev/null +++ b/docs/multiversx_sdk.delegation.rst @@ -0,0 +1,53 @@ +multiversx\_sdk.delegation package +================================== + +Submodules +---------- + +multiversx\_sdk.delegation.delegation\_controller module +-------------------------------------------------------- + +.. automodule:: multiversx_sdk.delegation.delegation_controller + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.delegation.delegation\_transactions\_factory module +------------------------------------------------------------------- + +.. automodule:: multiversx_sdk.delegation.delegation_transactions_factory + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.delegation.delegation\_transactions\_outcome\_parser module +--------------------------------------------------------------------------- + +.. automodule:: multiversx_sdk.delegation.delegation_transactions_outcome_parser + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.delegation.delegation\_transactions\_outcome\_parser\_types module +---------------------------------------------------------------------------------- + +.. automodule:: multiversx_sdk.delegation.delegation_transactions_outcome_parser_types + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.delegation.errors module +---------------------------------------- + +.. automodule:: multiversx_sdk.delegation.errors + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: multiversx_sdk.delegation + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/multiversx_sdk.network_providers.rst b/docs/multiversx_sdk.network_providers.rst index 74aa7df8..e93bac63 100644 --- a/docs/multiversx_sdk.network_providers.rst +++ b/docs/multiversx_sdk.network_providers.rst @@ -4,6 +4,14 @@ multiversx\_sdk.network\_providers package Submodules ---------- +multiversx\_sdk.network\_providers.account\_awaiter module +---------------------------------------------------------- + +.. automodule:: multiversx_sdk.network_providers.account_awaiter + :members: + :undoc-members: + :show-inheritance: + multiversx\_sdk.network\_providers.api\_network\_provider module ---------------------------------------------------------------- diff --git a/docs/multiversx_sdk.relayed.rst b/docs/multiversx_sdk.relayed.rst new file mode 100644 index 00000000..9a682683 --- /dev/null +++ b/docs/multiversx_sdk.relayed.rst @@ -0,0 +1,37 @@ +multiversx\_sdk.relayed package +=============================== + +Submodules +---------- + +multiversx\_sdk.relayed.errors module +------------------------------------- + +.. automodule:: multiversx_sdk.relayed.errors + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.relayed.relayed\_controller module +-------------------------------------------------- + +.. automodule:: multiversx_sdk.relayed.relayed_controller + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.relayed.relayed\_transactions\_factory module +------------------------------------------------------------- + +.. automodule:: multiversx_sdk.relayed.relayed_transactions_factory + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: multiversx_sdk.relayed + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/multiversx_sdk.rst b/docs/multiversx_sdk.rst index 4db166f2..7f083b94 100644 --- a/docs/multiversx_sdk.rst +++ b/docs/multiversx_sdk.rst @@ -8,11 +8,17 @@ Subpackages :maxdepth: 4 multiversx_sdk.abi + multiversx_sdk.account_management multiversx_sdk.accounts - multiversx_sdk.controllers + multiversx_sdk.builders multiversx_sdk.core + multiversx_sdk.delegation multiversx_sdk.entrypoints multiversx_sdk.network_providers + multiversx_sdk.relayed + multiversx_sdk.smart_contracts + multiversx_sdk.token_management + multiversx_sdk.transfers multiversx_sdk.wallet Module contents diff --git a/docs/multiversx_sdk.smart_contracts.rst b/docs/multiversx_sdk.smart_contracts.rst new file mode 100644 index 00000000..22dcc886 --- /dev/null +++ b/docs/multiversx_sdk.smart_contracts.rst @@ -0,0 +1,61 @@ +multiversx\_sdk.smart\_contracts package +======================================== + +Submodules +---------- + +multiversx\_sdk.smart\_contracts.errors module +---------------------------------------------- + +.. automodule:: multiversx_sdk.smart_contracts.errors + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.smart\_contracts.smart\_contract\_controller module +------------------------------------------------------------------- + +.. automodule:: multiversx_sdk.smart_contracts.smart_contract_controller + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.smart\_contracts.smart\_contract\_query module +-------------------------------------------------------------- + +.. automodule:: multiversx_sdk.smart_contracts.smart_contract_query + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.smart\_contracts.smart\_contract\_transactions\_factory module +------------------------------------------------------------------------------ + +.. automodule:: multiversx_sdk.smart_contracts.smart_contract_transactions_factory + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.smart\_contracts.smart\_contract\_transactions\_outcome\_parser module +-------------------------------------------------------------------------------------- + +.. automodule:: multiversx_sdk.smart_contracts.smart_contract_transactions_outcome_parser + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.smart\_contracts.smart\_contract\_transactions\_outcome\_parser\_types module +--------------------------------------------------------------------------------------------- + +.. automodule:: multiversx_sdk.smart_contracts.smart_contract_transactions_outcome_parser_types + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: multiversx_sdk.smart_contracts + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/multiversx_sdk.token_management.rst b/docs/multiversx_sdk.token_management.rst new file mode 100644 index 00000000..c6d19b1e --- /dev/null +++ b/docs/multiversx_sdk.token_management.rst @@ -0,0 +1,45 @@ +multiversx\_sdk.token\_management package +========================================= + +Submodules +---------- + +multiversx\_sdk.token\_management.token\_management\_controller module +---------------------------------------------------------------------- + +.. automodule:: multiversx_sdk.token_management.token_management_controller + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.token\_management.token\_management\_transactions\_factory module +--------------------------------------------------------------------------------- + +.. automodule:: multiversx_sdk.token_management.token_management_transactions_factory + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.token\_management.token\_management\_transactions\_outcome\_parser module +----------------------------------------------------------------------------------------- + +.. automodule:: multiversx_sdk.token_management.token_management_transactions_outcome_parser + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.token\_management.token\_management\_transactions\_outcome\_parser\_types module +------------------------------------------------------------------------------------------------ + +.. automodule:: multiversx_sdk.token_management.token_management_transactions_outcome_parser_types + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: multiversx_sdk.token_management + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/multiversx_sdk.transfers.rst b/docs/multiversx_sdk.transfers.rst new file mode 100644 index 00000000..77b8f784 --- /dev/null +++ b/docs/multiversx_sdk.transfers.rst @@ -0,0 +1,29 @@ +multiversx\_sdk.transfers package +================================= + +Submodules +---------- + +multiversx\_sdk.transfers.transfer\_transactions\_factory module +---------------------------------------------------------------- + +.. automodule:: multiversx_sdk.transfers.transfer_transactions_factory + :members: + :undoc-members: + :show-inheritance: + +multiversx\_sdk.transfers.transfers\_controller module +------------------------------------------------------ + +.. automodule:: multiversx_sdk.transfers.transfers_controller + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: multiversx_sdk.transfers + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/multiversx_sdk.wallet.rst b/docs/multiversx_sdk.wallet.rst index 81034b10..a0dfde00 100644 --- a/docs/multiversx_sdk.wallet.rst +++ b/docs/multiversx_sdk.wallet.rst @@ -37,6 +37,14 @@ multiversx\_sdk.wallet.interfaces module :undoc-members: :show-inheritance: +multiversx\_sdk.wallet.keypair module +------------------------------------- + +.. automodule:: multiversx_sdk.wallet.keypair + :members: + :undoc-members: + :show-inheritance: + multiversx\_sdk.wallet.mnemonic module -------------------------------------- From 9e80f48e8e2b9880dd33db3717a377b826636234 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 12 Dec 2024 15:27:12 +0200 Subject: [PATCH 13/17] update cookbook links --- examples/v1.ipynb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/v1.ipynb b/examples/v1.ipynb index 73fd2d46..c910e62d 100644 --- a/examples/v1.ipynb +++ b/examples/v1.ipynb @@ -353,7 +353,7 @@ "source": [ "A list of all the available methods from the `ApiNetworkProviders` can be found [here](https://multiversx.github.io/mx-sdk-py/multiversx_sdk.network_providers.html#module-multiversx_sdk.network_providers.api_network_provider).\n", "\n", - "Both the `ApiNetworkProvider` and the `ProxyNetworkProvider` implement a common interface, that can be seen [here](https://github.com/multiversx/mx-sdk-py/blob/feat/next/multiversx_sdk/network_providers/interface.py#L16), so, the two network providers can be used interchangeably.\n", + "Both the `ApiNetworkProvider` and the `ProxyNetworkProvider` implement a common interface, that can be seen [here](https://multiversx.github.io/mx-sdk-py/multiversx_sdk.network_providers.html#multiversx_sdk.network_providers.interface.INetworkProvider), so, the two network providers can be used interchangeably.\n", "\n", "The classes returned by the api have the most used fields easily accessible, but each object has a `raw` field where the raw api response is stored in case some other fields are needed." ] @@ -2554,8 +2554,8 @@ "metadata": {}, "source": [ "These are just a few examples of what we can do using the token management controller or factory. For a full list of what methods are supported for both, check out the autogenerated documentation:\n", - "- [TokenManagementController](#)\n", - "- [TokenManagementTransactionsFactory](#)\n", + "- [TokenManagementController](https://multiversx.github.io/mx-sdk-py/multiversx_sdk.token_management.html#module-multiversx_sdk.token_management.token_management_controller)\n", + "- [TokenManagementTransactionsFactory](https://multiversx.github.io/mx-sdk-py/multiversx_sdk.token_management.html#module-multiversx_sdk.token_management.token_management_transactions_factory)\n", "\n", "### Account management\n", "\n", @@ -2904,8 +2904,8 @@ "To read more about staking providers and delegation, please check out the [docs](https://docs.multiversx.com/validators/delegation-manager/#introducing-staking-providers). \n", "\n", "In this section, we are going to create a new delegation contract, get the address of the contract, delegate funds to the contract, redelegate rewards, claim rewards, undelegate and withdraw funds from the contract. The operations can be performed using both the `controller` and the `factory`. For a full list of all the methods supported check out the auto-generated documentation:\n", - "- [DelegationController](#)\n", - "- [DelegationTransactionsFactory](#)\n", + "- [DelegationController](https://multiversx.github.io/mx-sdk-py/multiversx_sdk.delegation.html#module-multiversx_sdk.delegation.delegation_controller)\n", + "- [DelegationTransactionsFactory](https://multiversx.github.io/mx-sdk-py/multiversx_sdk.delegation.html#module-multiversx_sdk.delegation.delegation_transactions_factory)\n", "\n", "#### Creating a new delegation contract using the controller" ] From a0a3b1513091826b88b154819a3641283c500ff3 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 13 Dec 2024 10:04:27 +0200 Subject: [PATCH 14/17] fix typo --- examples/v1.ipynb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/v1.ipynb b/examples/v1.ipynb index c910e62d..90dca8be 100644 --- a/examples/v1.ipynb +++ b/examples/v1.ipynb @@ -1376,7 +1376,7 @@ "# the user is responsible for managing the nonce\n", "account.nonce = entrypoint.recall_account_nonce(account.address)\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# get the smart contracts controller\n", @@ -1511,7 +1511,7 @@ "from multiversx_sdk.abi import Abi, BigUIntValue\n", "\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# get the smart contracts controller\n", @@ -1600,7 +1600,7 @@ "# the user is responsible for managing the nonce\n", "account.nonce = entrypoint.recall_account_nonce(account.address)\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# get the smart contracts controller\n", @@ -1678,7 +1678,7 @@ "# the user is responsible for managing the nonce\n", "account.nonce = entrypoint.recall_account_nonce(account.address)\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# get the smart contracts controller\n", @@ -1744,7 +1744,7 @@ "# the user is responsible for managing the nonce\n", "account.nonce = entrypoint.recall_account_nonce(account.address)\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# get the smart contracts controller\n", @@ -1801,7 +1801,7 @@ "from multiversx_sdk import DevnetEntrypoint, SmartContractTransactionsOutcomeParser\n", "from multiversx_sdk.abi import Abi\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# create the parser\n", @@ -1835,7 +1835,7 @@ "from multiversx_sdk import DevnetEntrypoint, TransactionEventsParser, find_events_by_first_topic\n", "from multiversx_sdk.abi import Abi\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/multisig-full.abi.json\"))\n", "\n", "# fetch the transaction of the network\n", @@ -1872,7 +1872,7 @@ "from multiversx_sdk import Address, DevnetEntrypoint\n", "from multiversx_sdk.abi import Abi\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# the contract address we'll query\n", @@ -1905,7 +1905,7 @@ "from multiversx_sdk import Address, DevnetEntrypoint\n", "from multiversx_sdk.abi import Abi\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# the contract address we'll query\n", @@ -1959,7 +1959,7 @@ "# the user is responsible for managing the nonce\n", "account.nonce = entrypoint.recall_account_nonce(account.address)\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# get the smart contracts controller\n", @@ -2016,7 +2016,7 @@ "from multiversx_sdk.abi import Abi, BigUIntValue\n", "\n", "\n", - "# load tha abi file\n", + "# load the abi file\n", "abi = Abi.load(Path(\"contracts/adder.abi.json\"))\n", "\n", "# get the smart contracts controller\n", From f8fb1c90bc12a0eb6d8e4859df049a833e8495c8 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 13 Dec 2024 16:20:16 +0200 Subject: [PATCH 15/17] tx hash as bytes for getting tx status --- multiversx_sdk/network_providers/proxy_network_provider.py | 6 ++++-- .../network_providers/proxy_network_provider_test.py | 2 +- multiversx_sdk/testutils/mock_network_provider.py | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/multiversx_sdk/network_providers/proxy_network_provider.py b/multiversx_sdk/network_providers/proxy_network_provider.py index 7bbbe4c1..5ea5ccc6 100644 --- a/multiversx_sdk/network_providers/proxy_network_provider.py +++ b/multiversx_sdk/network_providers/proxy_network_provider.py @@ -299,9 +299,11 @@ def query_contract(self, query: SmartContractQuery) -> SmartContractQueryRespons return vm_query_response_to_smart_contract_query_response(response, query.function) - def get_transaction_status(self, tx_hash: str) -> TransactionStatus: + def get_transaction_status(self, transaction_hash: Union[str, bytes]) -> TransactionStatus: """Fetches the status of a transaction.""" - response = self.do_get_generic(f'transaction/{tx_hash}/process-status') + transaction_hash = convert_tx_hash_to_string(transaction_hash) + + response = self.do_get_generic(f'transaction/{transaction_hash}/process-status') return TransactionStatus(response.get('status', '')) def do_get_generic(self, url: str, url_parameters: Optional[dict[str, Any]] = None) -> GenericResponse: diff --git a/multiversx_sdk/network_providers/proxy_network_provider_test.py b/multiversx_sdk/network_providers/proxy_network_provider_test.py index 17169eb6..235e12dd 100644 --- a/multiversx_sdk/network_providers/proxy_network_provider_test.py +++ b/multiversx_sdk/network_providers/proxy_network_provider_test.py @@ -145,7 +145,7 @@ def test_get_non_fungible_tokens_of_account(self): def test_get_transaction_status(self): result = self.proxy.get_transaction_status( - "9d47c4b4669cbcaa26f5dec79902dd20e55a0aa5f4b92454a74e7dbd0183ad6c" + bytes.fromhex("9d47c4b4669cbcaa26f5dec79902dd20e55a0aa5f4b92454a74e7dbd0183ad6c") ) assert result.status == "success" diff --git a/multiversx_sdk/testutils/mock_network_provider.py b/multiversx_sdk/testutils/mock_network_provider.py index 3c49baae..5b930980 100644 --- a/multiversx_sdk/testutils/mock_network_provider.py +++ b/multiversx_sdk/testutils/mock_network_provider.py @@ -161,8 +161,8 @@ def get_transaction(self, transaction_hash: Union[str, bytes]) -> TransactionOnN raise Exception("Transaction not found") - def get_transaction_status(self, tx_hash: str) -> TransactionStatus: - transaction = self.get_transaction(tx_hash) + def get_transaction_status(self, transaction_hash: Union[str, bytes]) -> TransactionStatus: + transaction = self.get_transaction(transaction_hash) return transaction.status def query_contract(self, query: SmartContractQuery) -> SmartContractQueryResponse: From e8201c9f38570c06390820603b9f16102a102df5 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 18 Dec 2024 15:29:16 +0200 Subject: [PATCH 16/17] cookbook fixes after review --- .pre-commit-config.yaml | 1 + examples/v1.ipynb | 179 +++++++++++++----- multiversx_sdk/__init__.py | 4 +- multiversx_sdk/network_providers/__init__.py | 4 +- .../network_providers/api_network_provider.py | 144 ++++++++------ .../api_network_provider_test.py | 14 +- multiversx_sdk/network_providers/interface.py | 8 +- .../proxy_network_provider.py | 173 ++++++++++------- .../proxy_network_provider_test.py | 21 +- multiversx_sdk/network_providers/resources.py | 7 - 10 files changed, 339 insertions(+), 216 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eacfe1ad..b09299e4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,6 +15,7 @@ repos: hooks: - id: isort name: isort (python) + args: ["--profile", "black", "--filter-files"] - repo: https://github.com/PyCQA/flake8 rev: 7.1.1 hooks: diff --git a/examples/v1.ipynb b/examples/v1.ipynb index 90dca8be..1f997faa 100644 --- a/examples/v1.ipynb +++ b/examples/v1.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -17,7 +17,7 @@ "source": [ "## Creating an Entrypoint\n", "\n", - "The Entrypoint represents a network client that makes the most common operations easily accessible. We have an entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint` and `LocalnetEntrypoint`. We are going to interact with the Devnet, so we create an entrypoint as follows:" + "The Entrypoint represents a network client that makes the most common operations easily accessible. We have an entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint` and `LocalnetEntrypoint`. For example, this is how to create a Devnet entrypoint:" ] }, { @@ -35,7 +35,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If we want to create an entrypoint that uses a third party api, we can do so as follows:" + "If we want to create an entrypoint that uses a third party API, we can do so as follows:" ] }, { @@ -53,7 +53,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "By default, an Entrypoint, in our case the `DevnetEntrypoint`, uses the api, but we can also create a custom one that interacts with the proxy." + "By default, an Entrypoint, in our case the `DevnetEntrypoint`, uses the API, but we can also create a custom one that interacts with the proxy." ] }, { @@ -177,7 +177,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Instantiating an Account from a KeyPair" + "#### Instantiating an Account from a KeyPair" ] }, { @@ -203,7 +203,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -217,6 +217,9 @@ "entrypoint = DevnetEntrypoint()\n", "account.nonce = entrypoint.recall_account_nonce(account.address)\n", "\n", + "# create any sort of transaction\n", + "...\n", + "\n", "# When needed, we can get the nonce and increment it\n", "nonce = account.get_nonce_then_increment()" ] @@ -227,7 +230,7 @@ "source": [ "### Saving the Account to a file\n", "\n", - "We can save the account to either a `pem` file or a `keystore` file. We discourage the use of PEM wallets for storing cryptocurrencies due to their lower security level. However, they prove to be highly convenient and user-friendly for application testing purposes.\n", + "We can save the account to either a `pem` file or a `keystore` file. **We discourage the use of PEM wallets for storing cryptocurrencies due to their lower security level.** However, they prove to be highly convenient and user-friendly for application testing purposes.\n", "\n", "#### Saving the Account for a PEM file" ] @@ -297,7 +300,7 @@ "- `get_transaction(tx_hash: str | bytes) -> TransactionOnNetwork;`\n", "- `await_transaction_completed(tx_hash: str | bytes) -> TransactionOnNetwork;`\n", "\n", - "Some other methods are exposed through a so called network provider. There are two types of network providers: ApiNetworkProvider and ProxyNetworkProvider. The ProxyNetworkProvider interacts directly with the proxy of an observing squad. The ApiNetworkProvider, as the name suggests, interacts with the api, that is a layer over the proxy. It fetches data from the network but also from Elastic Search." + "Some other methods are exposed through a so called network provider. There are two types of network providers: ApiNetworkProvider and ProxyNetworkProvider. The ProxyNetworkProvider interacts directly with the proxy of an observing squad. The ApiNetworkProvider, as the name suggests, interacts with the API, that is a layer over the proxy. It fetches data from the network but also from Elastic Search." ] }, { @@ -353,9 +356,9 @@ "source": [ "A list of all the available methods from the `ApiNetworkProviders` can be found [here](https://multiversx.github.io/mx-sdk-py/multiversx_sdk.network_providers.html#module-multiversx_sdk.network_providers.api_network_provider).\n", "\n", - "Both the `ApiNetworkProvider` and the `ProxyNetworkProvider` implement a common interface, that can be seen [here](https://multiversx.github.io/mx-sdk-py/multiversx_sdk.network_providers.html#multiversx_sdk.network_providers.interface.INetworkProvider), so, the two network providers can be used interchangeably.\n", + "Both the `ApiNetworkProvider` and the `ProxyNetworkProvider` implement a common interface, that can be seen [here](https://multiversx.github.io/mx-sdk-py/multiversx_sdk.network_providers.html#multiversx_sdk.network_providers.interface.INetworkProvider). Therefore, the two network providers can be used interchangeably.\n", "\n", - "The classes returned by the api have the most used fields easily accessible, but each object has a `raw` field where the raw api response is stored in case some other fields are needed." + "The classes returned by the API have the most used fields easily accessible, but each object has a `raw` field where the raw API response is stored in case some other fields are needed." ] }, { @@ -411,45 +414,84 @@ "source": [ "### Fetching a block from the network\n", "\n", - "We instantiate the args and we are going to fetch the block using it's hash. The `api` only supports fetching blocks by hash, while the `proxy` can fetch blocks by hash or by nonce. Keep in mind, that for the `proxy` the shard should also be specified in the arguments." + "We instantiate the args and we are going to fetch the block using it's hash. The `API` only supports fetching blocks by hash, while the `PROXY` can fetch blocks by hash or by nonce. Keep in mind, that for the `PROXY` the shard should also be specified in the arguments.\n", + "\n", + "#### Fetching a block using the API" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ - "from multiversx_sdk import DevnetEntrypoint, GetBlockArguments\n", + "from multiversx_sdk import ApiNetworkProvider\n", "\n", - "entrypoint = DevnetEntrypoint()\n", - "api = entrypoint.create_network_provider()\n", + "api = ApiNetworkProvider(\"https://devnet-api.multiversx.com\")\n", "\n", - "args = GetBlockArguments(block_hash=bytes.fromhex(\"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"))\n", - "block = api.get_block(arguments=args)" + "block_hash=\"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"\n", + "block = api.get_block(block_hash=block_hash)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Additionally, we can fetch the latest block from a specific shard:" + "Additionally, we can fetch the latest block from the network:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ - "from multiversx_sdk import DevnetEntrypoint, GetBlockArguments\n", + "from multiversx_sdk import ApiNetworkProvider\n", "\n", - "entrypoint = DevnetEntrypoint()\n", - "api = entrypoint.create_network_provider()\n", + "api = ApiNetworkProvider(\"https://devnet-api.multiversx.com\")\n", + "latest_block = api.get_latest_block()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Fetching a block using the PROXY\n", + "\n", + "When using the proxy, we have to provide the shard, as well." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import ProxyNetworkProvider\n", "\n", - "args = GetBlockArguments(block_hash=bytes.fromhex(\n", - " \"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"))\n", - "latest_block = api.get_latest_block(shard=1)" + "proxy = ProxyNetworkProvider(\"https://devnet-gateway.multiversx.com\")\n", + "\n", + "block_hash=\"1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a\"\n", + "block = proxy.get_block(shard=1, block_hash=block_hash)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also fetch the latest block from the network. The default shard will be the metachain, but we can specify a shard to fetch the latest block from." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from multiversx_sdk import ProxyNetworkProvider\n", + "\n", + "proxy = ProxyNetworkProvider(\"https://devnet-gateway.multiversx.com\")\n", + "block = proxy.get_latest_block()" ] }, { @@ -458,7 +500,7 @@ "source": [ "### Fetching an account\n", "\n", - "To fetch an account we'll need it's address. Once we have the address, we simply create an `Address` object and pass it as an argument to the method." + "To fetch an account we'll need its address. Once we have the address, we simply create an `Address` object and pass it as an argument to the method." ] }, { @@ -496,8 +538,8 @@ "entrypoint = DevnetEntrypoint()\n", "api = entrypoint.create_network_provider()\n", "\n", - "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", - "account = api.get_account_storage(address=alice)" + "address = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "account = api.get_account_storage(address=address)" ] }, { @@ -518,19 +560,19 @@ "entrypoint = DevnetEntrypoint()\n", "api = entrypoint.create_network_provider()\n", "\n", - "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", - "account = api.get_account_storage_entry(address=alice, entry_key=\"testKey\")" + "address = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "account = api.get_account_storage_entry(address=address, entry_key=\"testKey\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Waiting for an account to satisfy a condition\n", + "### Waiting for an account to meet a condition\n", "\n", - "There might be situations when we want to wait for a certain condition to be satisfied in order to proceed with some action. For example, I want to send 7 EGLD from Alice to Bob, but I can do so only when Alice has a balance of minimum 7 EGLD. This can help in situations in which you are waiting for someone to send some funds to Alice so then she can send further transfer the funds. \n", + "There are times when we need to wait for a specific condition to be met before proceeding with an action. For example, let's say we want to send 7 EGLD from Alice to Bob, but this can only happen once Alice's balance reaches at least 7 EGLD. This approach is useful in scenarios where you are waiting for external funds to be sent to Alice, allowing her to then transfer the required amount to another recipient.\n", "\n", - "We need to define our condition that will be checked each time the account is fetched from the network. For this, we create a function that takes as an argument an `AccountOnNetwork` object and return a `bool`.\n", + "We need to define our condition that will be checked each time the account is fetched from the network. For this, we create a function that takes as an argument an `AccountOnNetwork` object and returns a `bool`.\n", "\n", "Keep in mind that, this method has a default timeout that can be adjusted using the `AwaitingOptions` class." ] @@ -561,7 +603,7 @@ "source": [ "### Sending and simulating transactions\n", "\n", - "In order for our transactions to be executed, we use the network providers to broadcast them to the network. We'll discuss more about transactions in the dedicated section, but for now, we'll see a minimal example. Keep in mind that, in order for transactions to be processed they need to be signed.\n", + "In order for our transactions to be executed, we use the network providers to broadcast them to the network. Keep in mind that, in order for transactions to be processed they need to be signed.\n", "\n", "#### Sending a transaction" ] @@ -649,7 +691,7 @@ "source": [ "#### Simulating transactions\n", "\n", - "A transaction can be simulated before being sent to be processed by the network. It is mostly used for smart contract calls to see what smart contract results are produced. We'll discuss more about transactions in the dedicated section, but for now, we'll see minimal example." + "A transaction can be simulated before being sent to be processed by the network. It is mostly used for smart contract calls to see what smart contract results are produced." ] }, { @@ -682,7 +724,7 @@ "source": [ "#### Estimating the gas cost of a transaction\n", "\n", - "Before sending a transaction to the network to be processed, one can get the estimated gas limit that is required for the transaction to be executed. We'll discuss more about transactions in the dedicated section, but for now, we'll see minimal example." + "Before sending a transaction to the network to be processed, one can get the estimated gas limit that is required for the transaction to be executed." ] }, { @@ -849,7 +891,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Fetcing all non-fungible tokens of an account\n", + "### Fetching all non-fungible tokens of an account\n", "\n", "Fetches all non-fungible tokens held by an account. This method does not handle pagination, but can be achieved by using `do_get_generic`." ] @@ -903,7 +945,7 @@ "source": [ "### Querying Smart Contracts\n", "\n", - "Smart contract queries or view functions, are endpoints of a contract that only read data from the contract. We'll discuss more about smart contract queries in the Smart Contracts section, but for now, we use the network providers to send a query to the observer nodes." + "Smart contract queries or view functions, are endpoints of a contract that only read data from the contract. To send a query to the observer nodes, we can proceed as follows:" ] }, { @@ -931,7 +973,7 @@ "source": [ "### Custom Api/Proxy calls\n", "\n", - "The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and used ones. There might be times when custom api calls are needed. For that we have createad generic methods for both `GET` and `POST` requests.\n", + "The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and used ones. There might be times when custom API calls are needed. For that we have createad generic methods for both `GET` and `POST` requests.\n", "\n", "Let's assume we want to get all the transactions that are sent by Alice and call the `testFunction` function." ] @@ -962,7 +1004,7 @@ "source": [ "## Creating transactions\n", "\n", - "In this section, we'll learn how to create different types of transactions. For creating transactions, we can use `controllers` or `factories`. The `controllers` can be used for scripts or quick network interactions, while the `factories` provide a more granular approach, usually needed for DApps. Usually, the `controllers` use the same parameters as the `factories` but also take an `Account` and the `nonce` of the sender as arguments. The `controllers` also hold some extra functionality, like waiting for transaction completion and parsing transactions. The same functionality can be obtained for transactions built using the `factories` as well, we'll see how in the sections bellow. In the following section we'll learn how to create transactions using both." + "In this section, we'll learn how to create different types of transactions. For creating transactions, we can use `controllers` or `factories`. The `controllers` can be used for scripts or quick network interactions, while the `factories` provide a more granular and lower-level approach, usually needed for DApps. Usually, the `controllers` use the same parameters as the `factories` but also take an `Account` and the `nonce` of the sender as arguments. The `controllers` also hold some extra functionality, like waiting for transaction completion and parsing transactions. The same functionality can be obtained for transactions built using the `factories` as well, we'll see how in the sections bellow. In the following section we'll learn how to create transactions using both." ] }, { @@ -1047,7 +1089,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If you know youl'll only send native tokens, the same transaction can be created using the `create_transaction_for_native_token_transfer` method." + "If you know you'll only send native tokens, the same transaction can be created using the `create_transaction_for_native_token_transfer` method." ] }, { @@ -1098,7 +1140,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If you know youl'll only send native tokens, the same transaction can be created using the `create_transaction_for_native_token_transfer` method." + "If you know you'll only send native tokens, the same transaction can be created using the `create_transaction_for_native_token_transfer` method." ] }, { @@ -1151,7 +1193,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If you know youl'll only send ESDT tokens, the same transaction can be created using `create_transaction_for_esdt_token_transfer`." + "If you know you'll only send ESDT tokens, the same transaction can be created using `create_transaction_for_esdt_token_transfer`." ] }, { @@ -1211,7 +1253,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If you know youl'll only send ESDT tokens, the same transaction can be created using `create_transaction_for_esdt_token_transfer`.\n", + "If you know you'll only send ESDT tokens, the same transaction can be created using `create_transaction_for_esdt_token_transfer`.\n", "\n", "#### Sending native and custom tokens\n", "\n", @@ -3412,7 +3454,7 @@ "source": [ "### Relayed transactions\n", "\n", - "We are currently on the third iteration of relayed transactions. V1 and V2 are soon to be deactivated so we'll focus on V3. For V3, two new fields have been added on transactions: `releyer` and `relayerSignature`. Before the sender signs the transaction, the relayer needs to be set. After the sender has signed the transaction, the relayer can also sign the transaction and broadcast it. Keep in mind that, for relayed V3 transactions we need an extra `50_000` gas. Let's see how we can create a relayed transaction:" + "We are currently on the third iteration of relayed transactions. V1 and V2 are soon to be deactivated so we'll focus on V3. For V3, two new fields have been added on transactions: `relayer` and `relayerSignature`. Before the sender signs the transaction, the relayer needs to be set. After the sender has signed the transaction, the relayer can also sign the transaction and broadcast it. Keep in mind that, for relayed V3 transactions we need an extra `50_000` gas. Let's see how we can create a relayed transaction:" ] }, { @@ -3958,7 +4000,7 @@ "source": [ "#### Saving a secrey key to a PEM file\n", "\n", - "We can save a secret key to a pem file. This is not recommended as it is not secure, but it's very convenient for testing purposes." + "We can save a secret key to a pem file. **This is not recommended as it is not secure, but it's very convenient for testing purposes.**" ] }, { @@ -4270,7 +4312,7 @@ "\n", "The verification of a signature is done using the **public key** of an account. We have a few wrappers over public keys that make the verification of signatures a little bit easier.\n", "\n", - "#### Verifying a signature using a UserVerifier" + "#### Verifying Transaction signature using a UserVerifier" ] }, { @@ -4313,6 +4355,49 @@ "print(\"Transaction is signed by Alice:\", is_signed_by_alice)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Verifying Message signature using a UserVerifier" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "from multiversx_sdk import Account, Address, Message, MessageComputer, UserVerifier\n", + "\n", + "account = Account.new_from_pem(Path(\"../multiversx_sdk/testutils/testwallets/alice.pem\"))\n", + "\n", + "message = Message(\n", + " data=\"this is a test message\".encode(),\n", + " address=account.address\n", + ")\n", + "\n", + "# apply the signature on the message\n", + "message.signature = account.sign_message(message)\n", + "\n", + "# instantiating a user verifier; basically gets the public key\n", + "alice = Address.new_from_bech32(\"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th\")\n", + "alice_verifier = UserVerifier.from_address(alice)\n", + "\n", + "# serialize the message for verification\n", + "message_computer = MessageComputer()\n", + "serialized_message = message_computer.compute_bytes_for_verifying(message)\n", + "\n", + "# verify the signature\n", + "is_signed_by_alice = alice_verifier.verify(\n", + " data=serialized_message,\n", + " signature=message.signature\n", + ")\n", + "\n", + "print(\"Message is signed by Alice:\", is_signed_by_alice)" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/multiversx_sdk/__init__.py b/multiversx_sdk/__init__.py index 59539540..45de8798 100644 --- a/multiversx_sdk/__init__.py +++ b/multiversx_sdk/__init__.py @@ -26,7 +26,7 @@ BlockCoordinates, BlockOnNetwork, FungibleTokenMetadata, GenericError, GenericResponse, - GetBlockArguments, NetworkConfig, + NetworkConfig, NetworkProviderConfig, NetworkStatus, ProxyNetworkProvider, @@ -80,7 +80,7 @@ "FreezeOutcome", "UnFreezeOutcome", "WipeOutcome", "UpdateAttributesOutcome", "AddQuantityOutcome", "BurnQuantityOutcome", "TransactionOnNetwork", "TransactionStatus", "ParsedSmartContractCallOutcome", "AccountOnNetwork", "AccountStorage", "AccountStorageEntry", "AwaitingOptions", "BlockCoordinates", - "BlockOnNetwork", "FungibleTokenMetadata", "GetBlockArguments", "NetworkConfig", "NetworkStatus", + "BlockOnNetwork", "FungibleTokenMetadata", "NetworkConfig", "NetworkStatus", "TokenAmountOnNetwork", "TokensCollectionMetadata", "TransactionCostResponse", "AccountAwaiter", "LibraryConfig", "KeyPair" ] diff --git a/multiversx_sdk/network_providers/__init__.py b/multiversx_sdk/network_providers/__init__.py index 840a75c8..a429d0b8 100644 --- a/multiversx_sdk/network_providers/__init__.py +++ b/multiversx_sdk/network_providers/__init__.py @@ -8,7 +8,7 @@ from multiversx_sdk.network_providers.resources import ( AccountOnNetwork, AccountStorage, AccountStorageEntry, AwaitingOptions, BlockCoordinates, BlockOnNetwork, FungibleTokenMetadata, GenericResponse, - GetBlockArguments, NetworkConfig, NetworkStatus, TokenAmountOnNetwork, + NetworkConfig, NetworkStatus, TokenAmountOnNetwork, TokensCollectionMetadata, TransactionCostResponse) from multiversx_sdk.network_providers.transaction_awaiter import \ TransactionAwaiter @@ -21,6 +21,6 @@ "TransactionDecoder", "TransactionMetadata", "NetworkProviderConfig", "AccountOnNetwork", "AccountStorage", "AccountStorageEntry", "AwaitingOptions", "BlockCoordinates", "BlockOnNetwork", "FungibleTokenMetadata", - "GetBlockArguments", "NetworkConfig", "NetworkStatus", "TokenAmountOnNetwork", + "NetworkConfig", "NetworkStatus", "TokenAmountOnNetwork", "TokensCollectionMetadata", "TransactionCostResponse", "AccountAwaiter" ] diff --git a/multiversx_sdk/network_providers/api_network_provider.py b/multiversx_sdk/network_providers/api_network_provider.py index fe2b1c04..add10d90 100644 --- a/multiversx_sdk/network_providers/api_network_provider.py +++ b/multiversx_sdk/network_providers/api_network_provider.py @@ -3,47 +3,62 @@ import requests -from multiversx_sdk.core import (Address, Token, TokenComputer, Transaction, - TransactionOnNetwork) +from multiversx_sdk.core import Address, Token, TokenComputer, Transaction, TransactionOnNetwork from multiversx_sdk.core.config import LibraryConfig from multiversx_sdk.core.constants import METACHAIN_ID from multiversx_sdk.network_providers.account_awaiter import AccountAwaiter from multiversx_sdk.network_providers.config import NetworkProviderConfig from multiversx_sdk.network_providers.constants import ( - BASE_USER_AGENT, DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS) -from multiversx_sdk.network_providers.errors import (GenericError, - TransactionFetchingError) + BASE_USER_AGENT, + DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS, +) +from multiversx_sdk.network_providers.errors import GenericError, TransactionFetchingError from multiversx_sdk.network_providers.http_resources import ( - account_from_api_response, account_storage_entry_from_response, - account_storage_from_response, block_from_response, + account_from_api_response, + account_storage_entry_from_response, + account_storage_from_response, + block_from_response, definition_of_fungible_token_from_api_response, definition_of_tokens_collection_from_api_response, - smart_contract_query_to_vm_query_request, token_amount_from_api_response, - transaction_cost_estimation_from_response, transaction_from_api_response, + smart_contract_query_to_vm_query_request, + token_amount_from_api_response, + transaction_cost_estimation_from_response, + transaction_from_api_response, transaction_from_simulate_response, transactions_from_send_multiple_response, - vm_query_response_to_smart_contract_query_response) + vm_query_response_to_smart_contract_query_response, +) from multiversx_sdk.network_providers.interface import INetworkProvider -from multiversx_sdk.network_providers.proxy_network_provider import \ - ProxyNetworkProvider +from multiversx_sdk.network_providers.proxy_network_provider import ProxyNetworkProvider from multiversx_sdk.network_providers.resources import ( - AccountOnNetwork, AccountStorage, AccountStorageEntry, AwaitingOptions, - BlockOnNetwork, FungibleTokenMetadata, GetBlockArguments, NetworkConfig, - NetworkStatus, TokenAmountOnNetwork, TokensCollectionMetadata, - TransactionCostResponse) + AccountOnNetwork, + AccountStorage, + AccountStorageEntry, + AwaitingOptions, + BlockOnNetwork, + FungibleTokenMetadata, + NetworkConfig, + NetworkStatus, + TokenAmountOnNetwork, + TokensCollectionMetadata, + TransactionCostResponse, +) from multiversx_sdk.network_providers.shared import convert_tx_hash_to_string -from multiversx_sdk.network_providers.transaction_awaiter import \ - TransactionAwaiter +from multiversx_sdk.network_providers.transaction_awaiter import TransactionAwaiter from multiversx_sdk.network_providers.user_agent import extend_user_agent from multiversx_sdk.smart_contracts.smart_contract_query import ( - SmartContractQuery, SmartContractQueryResponse) + SmartContractQuery, + SmartContractQueryResponse, +) class ApiNetworkProvider(INetworkProvider): - def __init__(self, - url: str, - address_hrp: Optional[str] = None, - config: Optional[NetworkProviderConfig] = None) -> None: + def __init__( + self, + url: str, + address_hrp: Optional[str] = None, + config: Optional[NetworkProviderConfig] = None, + ) -> None: self.url = url self.address_hrp = address_hrp or LibraryConfig.default_address_hrp self.backing_proxy = ProxyNetworkProvider(url, self.address_hrp) @@ -60,22 +75,21 @@ def get_network_status(self, shard: int = METACHAIN_ID) -> NetworkStatus: """Fetches the current status of the network.""" return self.backing_proxy.get_network_status(shard) - def get_block(self, arguments: GetBlockArguments) -> BlockOnNetwork: + def get_block(self, block_hash: Union[str, bytes]) -> BlockOnNetwork: """Fetches a block by hash.""" - if not arguments.block_hash: - raise Exception("Block hash not provided. Please set the `block_hash` in the arguments.") + block_hash = block_hash.hex() if isinstance(block_hash, bytes) else block_hash - result = self.do_get_generic(f"blocks/{arguments.block_hash.hex()}") + result = self.do_get_generic(f"blocks/{block_hash}") return block_from_response(result) - def get_latest_block(self, shard: Optional[int] = None) -> BlockOnNetwork: + def get_latest_block(self) -> BlockOnNetwork: """Fetches the latest block of a shard.""" result = self.do_get_generic("/blocks/latest") return block_from_response(result) def get_account(self, address: Address) -> AccountOnNetwork: """Fetches account information for a given address.""" - response = self.do_get_generic(f'accounts/{address.to_bech32()}') + response = self.do_get_generic(f"accounts/{address.to_bech32()}") account = account_from_api_response(response) return account @@ -90,22 +104,28 @@ def get_account_storage(self, address: Address) -> AccountStorage: def get_account_storage_entry(self, address: Address, entry_key: str) -> AccountStorageEntry: """Fetches a specific storage entry of an account.""" key_as_hex = entry_key.encode().hex() - response: dict[str, Any] = self.do_get_generic(f"address/{address.to_bech32()}/key/{key_as_hex}") + response: dict[str, Any] = self.do_get_generic( + f"address/{address.to_bech32()}/key/{key_as_hex}" + ) return account_storage_entry_from_response(response.get("data", {}), entry_key) def await_account_on_condition( - self, address: Address, condition: Callable[[AccountOnNetwork], - bool], - options: Optional[AwaitingOptions] = None) -> AccountOnNetwork: + self, + address: Address, + condition: Callable[[AccountOnNetwork], bool], + options: Optional[AwaitingOptions] = None, + ) -> AccountOnNetwork: """Waits until an account satisfies a given condition.""" if options is None: - options = AwaitingOptions(patience_in_milliseconds=DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS) + options = AwaitingOptions( + patience_in_milliseconds=DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS + ) awaiter = AccountAwaiter( fetcher=self, polling_interval_in_milliseconds=options.polling_interval_in_milliseconds, timeout_interval_in_milliseconds=options.timeout_in_milliseconds, - patience_time_in_milliseconds=options.patience_in_milliseconds + patience_time_in_milliseconds=options.patience_in_milliseconds, ) return awaiter.await_on_condition(address=address, condition=condition) @@ -113,21 +133,27 @@ def await_account_on_condition( def send_transaction(self, transaction: Transaction) -> bytes: """Broadcasts a transaction and returns its hash.""" response = self.do_post_generic("transactions", transaction.to_dictionary()) - return bytes.fromhex(response.get('txHash', '')) + return bytes.fromhex(response.get("txHash", "")) - def simulate_transaction(self, transaction: Transaction, check_signature: bool = False) -> TransactionOnNetwork: + def simulate_transaction( + self, transaction: Transaction, check_signature: bool = False + ) -> TransactionOnNetwork: """Simulates a transaction.""" - url = 'transaction/simulate?checkSignature=false' + url = "transaction/simulate?checkSignature=false" if check_signature: - url = 'transaction/simulate' + url = "transaction/simulate" response: dict[str, Any] = self.do_post_generic(url, transaction.to_dictionary()) - return transaction_from_simulate_response(transaction, response.get("data", {}).get("result", {})) + return transaction_from_simulate_response( + transaction, response.get("data", {}).get("result", {}) + ) def estimate_transaction_cost(self, transaction: Transaction) -> TransactionCostResponse: """Estimates the cost of a transaction.""" - response: dict[str, Any] = self.do_post_generic('transaction/cost', transaction.to_dictionary()) + response: dict[str, Any] = self.do_post_generic( + "transaction/cost", transaction.to_dictionary() + ) return transaction_cost_estimation_from_response(response.get("data", {})) def send_transactions(self, transactions: list[Transaction]) -> tuple[int, list[bytes]]: @@ -137,21 +163,23 @@ def send_transactions(self, transactions: list[Transaction]) -> tuple[int, list[ If a transaction is not accepted, its hash is empty in the returned list. """ transactions_as_dictionaries = [transaction.to_dictionary() for transaction in transactions] - response: dict[str, Any] = self.do_post_generic('transaction/send-multiple', transactions_as_dictionaries) + response: dict[str, Any] = self.do_post_generic( + "transaction/send-multiple", transactions_as_dictionaries + ) return transactions_from_send_multiple_response(response.get("data", {}), len(transactions)) def get_transaction(self, transaction_hash: Union[str, bytes]) -> TransactionOnNetwork: """Fetches a transaction that was previously broadcasted (maybe already processed by the network).""" transaction_hash = convert_tx_hash_to_string(transaction_hash) try: - response = self.do_get_generic(f'transactions/{transaction_hash}') + response = self.do_get_generic(f"transactions/{transaction_hash}") except GenericError as ge: raise TransactionFetchingError(ge.url, ge.data) return transaction_from_api_response(transaction_hash, response) def await_transaction_completed( - self, transaction_hash: Union[str, bytes], - options: Optional[AwaitingOptions] = None) -> TransactionOnNetwork: + self, transaction_hash: Union[str, bytes], options: Optional[AwaitingOptions] = None + ) -> TransactionOnNetwork: """Waits until the transaction is completely processed.""" transaction_hash = convert_tx_hash_to_string(transaction_hash) @@ -162,16 +190,17 @@ def await_transaction_completed( fetcher=self, polling_interval_in_milliseconds=options.polling_interval_in_milliseconds, timeout_interval_in_milliseconds=options.timeout_in_milliseconds, - patience_time_in_milliseconds=options.patience_in_milliseconds + patience_time_in_milliseconds=options.patience_in_milliseconds, ) return awaiter.await_completed(transaction_hash) def await_transaction_on_condition( - self, transaction_hash: Union[str, bytes], - condition: Callable[[TransactionOnNetwork], - bool], - options: Optional[AwaitingOptions] = None) -> TransactionOnNetwork: + self, + transaction_hash: Union[str, bytes], + condition: Callable[[TransactionOnNetwork], bool], + options: Optional[AwaitingOptions] = None, + ) -> TransactionOnNetwork: """Waits until a transaction satisfies a given condition.""" transaction_hash = convert_tx_hash_to_string(transaction_hash) @@ -182,7 +211,7 @@ def await_transaction_on_condition( fetcher=self, polling_interval_in_milliseconds=options.polling_interval_in_milliseconds, timeout_interval_in_milliseconds=options.timeout_in_milliseconds, - patience_time_in_milliseconds=options.patience_in_milliseconds + patience_time_in_milliseconds=options.patience_in_milliseconds, ) return awaiter.await_on_condition(transaction_hash, condition) @@ -196,7 +225,9 @@ def get_token_of_account(self, address: Address, token: Token) -> TokenAmountOnN identifier = TokenComputer().compute_extended_identifier(token) result = self.do_get_generic(f"accounts/{address.to_bech32()}/nfts/{identifier}") else: - result = self.do_get_generic(f"accounts/{address.to_bech32()}/tokens/{token.identifier}") + result = self.do_get_generic( + f"accounts/{address.to_bech32()}/tokens/{token.identifier}" + ) return token_amount_from_api_response(result) @@ -228,12 +259,12 @@ def get_definition_of_tokens_collection(self, collection_name: str) -> TokensCol def query_contract(self, query: SmartContractQuery) -> SmartContractQueryResponse: request = smart_contract_query_to_vm_query_request(query) - response = self.do_post_generic('query', request) + response = self.do_post_generic("query", request) return vm_query_response_to_smart_contract_query_response(response, query.function) def do_get_generic(self, url: str, url_parameters: Optional[dict[str, Any]] = None) -> Any: """Does a generic GET request against the network(handles API enveloping).""" - url = f'{self.url}/{url}' + url = f"{self.url}/{url}" if url_parameters is not None: params = urllib.parse.urlencode(url_parameters) @@ -243,9 +274,10 @@ def do_get_generic(self, url: str, url_parameters: Optional[dict[str, Any]] = No return response def do_post_generic( - self, url: str, data: Any, url_parameters: Optional[dict[str, Any]] = None) -> Any: + self, url: str, data: Any, url_parameters: Optional[dict[str, Any]] = None + ) -> Any: """Does a generic GET request against the network(handles API enveloping).""" - url = f'{self.url}/{url}' + url = f"{self.url}/{url}" if url_parameters is not None: params = urllib.parse.urlencode(url_parameters) diff --git a/multiversx_sdk/network_providers/api_network_provider_test.py b/multiversx_sdk/network_providers/api_network_provider_test.py index 8403f186..3d01bff1 100644 --- a/multiversx_sdk/network_providers/api_network_provider_test.py +++ b/multiversx_sdk/network_providers/api_network_provider_test.py @@ -9,8 +9,7 @@ from multiversx_sdk.network_providers.config import NetworkProviderConfig from multiversx_sdk.network_providers.http_resources import \ account_from_api_response -from multiversx_sdk.network_providers.resources import (GetBlockArguments, - TokenAmountOnNetwork) +from multiversx_sdk.network_providers.resources import TokenAmountOnNetwork from multiversx_sdk.smart_contracts.smart_contract_query import \ SmartContractQuery from multiversx_sdk.testutils.wallets import load_wallets @@ -41,15 +40,8 @@ def test_get_network_status(self): assert result.raw def test_get_block(self): - args = GetBlockArguments(block_nonce=5949242) - - with pytest.raises(Exception, match="Block hash not provided. Please set the `block_hash` in the arguments."): - self.api.get_block(args) - - args = GetBlockArguments( - block_hash=bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f") - ) - result_by_hash = self.api.get_block(args) + block_hash=bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f") + result_by_hash = self.api.get_block(block_hash) assert result_by_hash.hash == bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f") assert result_by_hash.nonce == 5949242 diff --git a/multiversx_sdk/network_providers/interface.py b/multiversx_sdk/network_providers/interface.py index 1056e12a..4c5f12a8 100644 --- a/multiversx_sdk/network_providers/interface.py +++ b/multiversx_sdk/network_providers/interface.py @@ -6,7 +6,7 @@ from multiversx_sdk.core.transaction_on_network import TransactionOnNetwork from multiversx_sdk.network_providers.resources import ( AccountOnNetwork, AccountStorage, AccountStorageEntry, AwaitingOptions, - BlockOnNetwork, FungibleTokenMetadata, GetBlockArguments, NetworkConfig, + BlockOnNetwork, FungibleTokenMetadata, NetworkConfig, NetworkStatus, TokenAmountOnNetwork, TokensCollectionMetadata, TransactionCostResponse) from multiversx_sdk.smart_contracts.smart_contract_query import ( @@ -20,12 +20,6 @@ def get_network_config(self) -> NetworkConfig: def get_network_status(self, shard: int) -> NetworkStatus: ... - def get_block(self, arguments: GetBlockArguments) -> BlockOnNetwork: - ... - - def get_latest_block(self, shard: int) -> BlockOnNetwork: - ... - def get_account(self, address: Address) -> AccountOnNetwork: ... diff --git a/multiversx_sdk/network_providers/proxy_network_provider.py b/multiversx_sdk/network_providers/proxy_network_provider.py index 5ea5ccc6..0025b925 100644 --- a/multiversx_sdk/network_providers/proxy_network_provider.py +++ b/multiversx_sdk/network_providers/proxy_network_provider.py @@ -7,8 +7,7 @@ from multiversx_sdk.core.address import Address from multiversx_sdk.core.config import LibraryConfig -from multiversx_sdk.core.constants import (ESDT_CONTRACT_ADDRESS_HEX, - METACHAIN_ID) +from multiversx_sdk.core.constants import ESDT_CONTRACT_ADDRESS_HEX, METACHAIN_ID from multiversx_sdk.core.tokens import Token from multiversx_sdk.core.transaction import Transaction from multiversx_sdk.core.transaction_on_network import TransactionOnNetwork @@ -16,41 +15,59 @@ from multiversx_sdk.network_providers.account_awaiter import AccountAwaiter from multiversx_sdk.network_providers.config import NetworkProviderConfig from multiversx_sdk.network_providers.constants import ( - BASE_USER_AGENT, DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS) -from multiversx_sdk.network_providers.errors import (GenericError, - TransactionFetchingError) + BASE_USER_AGENT, + DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS, +) +from multiversx_sdk.network_providers.errors import GenericError, TransactionFetchingError from multiversx_sdk.network_providers.http_resources import ( - account_from_proxy_response, account_storage_entry_from_response, - account_storage_from_response, block_from_response, + account_from_proxy_response, + account_storage_entry_from_response, + account_storage_from_response, + block_from_response, definition_of_fungible_token_from_query_response, definition_of_tokens_collection_from_query_response, - network_config_from_response, network_status_from_response, + network_config_from_response, + network_status_from_response, smart_contract_query_to_vm_query_request, token_amount_on_network_from_proxy_response, token_amounts_from_proxy_response, - transaction_cost_estimation_from_response, transaction_from_proxy_response, + transaction_cost_estimation_from_response, + transaction_from_proxy_response, transaction_from_simulate_response, transactions_from_send_multiple_response, - vm_query_response_to_smart_contract_query_response) + vm_query_response_to_smart_contract_query_response, +) from multiversx_sdk.network_providers.interface import INetworkProvider from multiversx_sdk.network_providers.resources import ( - AccountOnNetwork, AccountStorage, AccountStorageEntry, AwaitingOptions, - BlockOnNetwork, FungibleTokenMetadata, GenericResponse, GetBlockArguments, - NetworkConfig, NetworkStatus, TokenAmountOnNetwork, - TokensCollectionMetadata, TransactionCostResponse) + AccountOnNetwork, + AccountStorage, + AccountStorageEntry, + AwaitingOptions, + BlockOnNetwork, + FungibleTokenMetadata, + GenericResponse, + NetworkConfig, + NetworkStatus, + TokenAmountOnNetwork, + TokensCollectionMetadata, + TransactionCostResponse, +) from multiversx_sdk.network_providers.shared import convert_tx_hash_to_string -from multiversx_sdk.network_providers.transaction_awaiter import \ - TransactionAwaiter +from multiversx_sdk.network_providers.transaction_awaiter import TransactionAwaiter from multiversx_sdk.network_providers.user_agent import extend_user_agent from multiversx_sdk.smart_contracts.smart_contract_query import ( - SmartContractQuery, SmartContractQueryResponse) + SmartContractQuery, + SmartContractQueryResponse, +) class ProxyNetworkProvider(INetworkProvider): - def __init__(self, - url: str, - address_hrp: Optional[str] = None, - config: Optional[NetworkProviderConfig] = None) -> None: + def __init__( + self, + url: str, + address_hrp: Optional[str] = None, + config: Optional[NetworkProviderConfig] = None, + ) -> None: self.url = url self.address_hrp = address_hrp or LibraryConfig.default_address_hrp self.config = config if config is not None else NetworkProviderConfig() @@ -60,25 +77,28 @@ def __init__(self, def get_network_config(self) -> NetworkConfig: """Fetches the general configuration of the network.""" - response = self.do_get_generic('network/config') - return network_config_from_response(response.get('config', {})) + response = self.do_get_generic("network/config") + return network_config_from_response(response.get("config", {})) def get_network_status(self, shard: int = METACHAIN_ID) -> NetworkStatus: """Fetches the current status of the network.""" - response = self.do_get_generic(f'network/status/{shard}') - return network_status_from_response(response.get('status', '')) - - def get_block(self, arguments: GetBlockArguments) -> BlockOnNetwork: + response = self.do_get_generic(f"network/status/{shard}") + return network_status_from_response(response.get("status", "")) + + def get_block( + self, + shard: int, + block_hash: Optional[Union[str, bytes]] = None, + block_nonce: Optional[int] = None, + ) -> BlockOnNetwork: """Fetches a block by nonce or by hash.""" - if not arguments.shard: - raise Exception("Shard not provided. Please set the shard in the arguments.") - - if arguments.block_hash: - response = self.do_get_generic(f"block/{arguments.shard}/by-hash/{arguments.block_hash.hex()}") - elif arguments.block_nonce: - response = self.do_get_generic(f"block/{arguments.shard}/by-nonce/{arguments.block_nonce}") + if block_hash: + block_hash = block_hash.hex() if isinstance(block_hash, bytes) else block_hash + response = self.do_get_generic(f"block/{shard}/by-hash/{block_hash}") + elif block_nonce: + response = self.do_get_generic(f"block/{shard}/by-nonce/{block_nonce}") else: - raise Exception("Block hash or block nonce not provided") + raise Exception("Block hash or block nonce not provided.") return block_from_response(response.get("block", {})) @@ -95,7 +115,7 @@ def get_account(self, address: Address) -> AccountOnNetwork: get_guardian_data_thread = Thread(target=self._get_guardian_data, args=(address, data)) get_guardian_data_thread.start() - response = self.do_get_generic(f'address/{address.to_bech32()}') + response = self.do_get_generic(f"address/{address.to_bech32()}") account = account_from_proxy_response(response.to_dictionary()) get_guardian_data_thread.join(timeout=2) @@ -122,42 +142,48 @@ def get_account_storage_entry(self, address: Address, entry_key: str) -> Account return account_storage_entry_from_response(response.to_dictionary(), entry_key) def await_account_on_condition( - self, address: Address, condition: Callable[[AccountOnNetwork], - bool], - options: Optional[AwaitingOptions] = None) -> AccountOnNetwork: + self, + address: Address, + condition: Callable[[AccountOnNetwork], bool], + options: Optional[AwaitingOptions] = None, + ) -> AccountOnNetwork: """Waits until an account satisfies a given condition.""" if options is None: - options = AwaitingOptions(patience_in_milliseconds=DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS) + options = AwaitingOptions( + patience_in_milliseconds=DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS + ) awaiter = AccountAwaiter( fetcher=self, polling_interval_in_milliseconds=options.polling_interval_in_milliseconds, timeout_interval_in_milliseconds=options.timeout_in_milliseconds, - patience_time_in_milliseconds=options.patience_in_milliseconds + patience_time_in_milliseconds=options.patience_in_milliseconds, ) return awaiter.await_on_condition(address=address, condition=condition) def send_transaction(self, transaction: Transaction) -> bytes: """Broadcasts a transaction and returns its hash.""" - response = self.do_post_generic( - 'transaction/send', transaction.to_dictionary()) - return bytes.fromhex(response.get('txHash', '')) + response = self.do_post_generic("transaction/send", transaction.to_dictionary()) + return bytes.fromhex(response.get("txHash", "")) - def simulate_transaction(self, transaction: Transaction, check_signature: bool = False) -> TransactionOnNetwork: + def simulate_transaction( + self, transaction: Transaction, check_signature: bool = False + ) -> TransactionOnNetwork: """Simulates a transaction.""" - url = 'transaction/simulate?checkSignature=false' + url = "transaction/simulate?checkSignature=false" if check_signature: - url = 'transaction/simulate' + url = "transaction/simulate" response = self.do_post_generic(url, transaction.to_dictionary()) - return transaction_from_simulate_response(transaction, response.to_dictionary().get("result", {})) + return transaction_from_simulate_response( + transaction, response.to_dictionary().get("result", {}) + ) def estimate_transaction_cost(self, transaction: Transaction) -> TransactionCostResponse: """Estimates the cost of a transaction.""" - response = self.do_post_generic( - 'transaction/cost', transaction.to_dictionary()) + response = self.do_post_generic("transaction/cost", transaction.to_dictionary()) return transaction_cost_estimation_from_response(response.to_dictionary()) def send_transactions(self, transactions: list[Transaction]) -> tuple[int, list[bytes]]: @@ -167,7 +193,7 @@ def send_transactions(self, transactions: list[Transaction]) -> tuple[int, list[ If a transaction is not accepted, its hash is empty in the returned list. """ transactions_as_dictionaries = [transaction.to_dictionary() for transaction in transactions] - response = self.do_post_generic('transaction/send-multiple', transactions_as_dictionaries) + response = self.do_post_generic("transaction/send-multiple", transactions_as_dictionaries) return transactions_from_send_multiple_response(response.to_dictionary(), len(transactions)) def get_transaction(self, transaction_hash: Union[bytes, str]) -> TransactionOnNetwork: @@ -176,7 +202,7 @@ def get_transaction(self, transaction_hash: Union[bytes, str]) -> TransactionOnN def get_tx() -> dict[str, Any]: url = f"transaction/{transaction_hash}?withResults=true" - return self.do_get_generic(url).get('transaction', '') + return self.do_get_generic(url).get("transaction", "") status_task = None with ThreadPoolExecutor(max_workers=2) as executor: @@ -195,8 +221,8 @@ def get_tx() -> dict[str, Any]: return transaction_from_proxy_response(transaction_hash, tx, process_status) def await_transaction_completed( - self, transaction_hash: Union[bytes, str], - options: Optional[AwaitingOptions] = None) -> TransactionOnNetwork: + self, transaction_hash: Union[bytes, str], options: Optional[AwaitingOptions] = None + ) -> TransactionOnNetwork: """Waits until the transaction is completely processed.""" transaction_hash = convert_tx_hash_to_string(transaction_hash) @@ -207,15 +233,17 @@ def await_transaction_completed( fetcher=self, polling_interval_in_milliseconds=options.polling_interval_in_milliseconds, timeout_interval_in_milliseconds=options.timeout_in_milliseconds, - patience_time_in_milliseconds=options.patience_in_milliseconds + patience_time_in_milliseconds=options.patience_in_milliseconds, ) return awaiter.await_completed(transaction_hash) - def await_transaction_on_condition(self, - transaction_hash: Union[str, bytes], - condition: Callable[[TransactionOnNetwork], bool], - options: Optional[AwaitingOptions] = None) -> TransactionOnNetwork: + def await_transaction_on_condition( + self, + transaction_hash: Union[str, bytes], + condition: Callable[[TransactionOnNetwork], bool], + options: Optional[AwaitingOptions] = None, + ) -> TransactionOnNetwork: """Waits until a transaction satisfies a given condition.""" transaction_hash = convert_tx_hash_to_string(transaction_hash) @@ -226,7 +254,7 @@ def await_transaction_on_condition(self, fetcher=self, polling_interval_in_milliseconds=options.polling_interval_in_milliseconds, timeout_interval_in_milliseconds=options.timeout_in_milliseconds, - patience_time_in_milliseconds=options.patience_in_milliseconds + patience_time_in_milliseconds=options.patience_in_milliseconds, ) return awaiter.await_on_condition(transaction_hash, condition) @@ -239,7 +267,9 @@ def get_token_of_account(self, address: Address, token: Token) -> TokenAmountOnN if token.nonce == 0: response = self.do_get_generic(f"address/{address.to_bech32()}/esdt/{token.identifier}") else: - response = self.do_get_generic(f"address/{address.to_bech32()}/nft/{token.identifier}/nonce/{token.nonce}") + response = self.do_get_generic( + f"address/{address.to_bech32()}/nft/{token.identifier}/nonce/{token.nonce}" + ) return token_amount_on_network_from_proxy_response(response.to_dictionary()) @@ -294,21 +324,23 @@ def get_definition_of_tokens_collection(self, collection_name: str) -> TokensCol def query_contract(self, query: SmartContractQuery) -> SmartContractQueryResponse: """Queries a smart contract.""" request = smart_contract_query_to_vm_query_request(query) - response = self.do_post_generic('vm-values/query', request) - response = response.get('data', '') + response = self.do_post_generic("vm-values/query", request) + response = response.get("data", "") return vm_query_response_to_smart_contract_query_response(response, query.function) def get_transaction_status(self, transaction_hash: Union[str, bytes]) -> TransactionStatus: """Fetches the status of a transaction.""" transaction_hash = convert_tx_hash_to_string(transaction_hash) - - response = self.do_get_generic(f'transaction/{transaction_hash}/process-status') - return TransactionStatus(response.get('status', '')) - def do_get_generic(self, url: str, url_parameters: Optional[dict[str, Any]] = None) -> GenericResponse: + response = self.do_get_generic(f"transaction/{transaction_hash}/process-status") + return TransactionStatus(response.get("status", "")) + + def do_get_generic( + self, url: str, url_parameters: Optional[dict[str, Any]] = None + ) -> GenericResponse: """Does a generic GET request against the network (handles API enveloping).""" - url = f'{self.url}/{url}' + url = f"{self.url}/{url}" if url_parameters is not None: params = urllib.parse.urlencode(url_parameters) @@ -318,9 +350,10 @@ def do_get_generic(self, url: str, url_parameters: Optional[dict[str, Any]] = No return response def do_post_generic( - self, url: str, data: Any, url_parameters: Optional[dict[str, Any]] = None) -> GenericResponse: + self, url: str, data: Any, url_parameters: Optional[dict[str, Any]] = None + ) -> GenericResponse: """Does a generic GET request against the network (handles API enveloping).""" - url = f'{self.url}/{url}' + url = f"{self.url}/{url}" if url_parameters is not None: params = urllib.parse.urlencode(url_parameters) diff --git a/multiversx_sdk/network_providers/proxy_network_provider_test.py b/multiversx_sdk/network_providers/proxy_network_provider_test.py index 235e12dd..0ed565eb 100644 --- a/multiversx_sdk/network_providers/proxy_network_provider_test.py +++ b/multiversx_sdk/network_providers/proxy_network_provider_test.py @@ -8,8 +8,7 @@ from multiversx_sdk.network_providers.http_resources import block_from_response from multiversx_sdk.network_providers.proxy_network_provider import \ ProxyNetworkProvider -from multiversx_sdk.network_providers.resources import (GetBlockArguments, - TokenAmountOnNetwork) +from multiversx_sdk.network_providers.resources import TokenAmountOnNetwork from multiversx_sdk.smart_contracts.smart_contract_query import \ SmartContractQuery from multiversx_sdk.testutils.wallets import load_wallets @@ -40,19 +39,13 @@ def test_get_network_status(self): assert result.raw def test_get_block(self): - args = GetBlockArguments(block_nonce=5949242) + shard=1 + + block_hash=bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f") + result_by_hash = self.proxy.get_block(shard=shard, block_hash=block_hash) - with pytest.raises(Exception, match="Shard not provided. Please set the shard in the arguments."): - self.proxy.get_block(args) - - args = GetBlockArguments( - shard=1, block_hash=bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f") - ) - result_by_hash = self.proxy.get_block(args) - - args = GetBlockArguments( - shard=1, block_nonce=5949242) - result_by_nonce = self.proxy.get_block(args) + block_nonce=5949242 + result_by_nonce = self.proxy.get_block(shard=shard, block_nonce=block_nonce) assert result_by_hash.hash == bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f") assert result_by_hash.nonce == 5949242 diff --git a/multiversx_sdk/network_providers/resources.py b/multiversx_sdk/network_providers/resources.py index a92a2371..c768d83b 100644 --- a/multiversx_sdk/network_providers/resources.py +++ b/multiversx_sdk/network_providers/resources.py @@ -139,10 +139,3 @@ class AwaitingOptions: polling_interval_in_milliseconds: int = DEFAULT_TRANSACTION_AWAITING_POLLING_TIMEOUT_IN_MILLISECONDS timeout_in_milliseconds: int = DEFAULT_TRANSACTION_AWAITING_TIMEOUT_IN_MILLISECONDS patience_in_milliseconds: int = DEFAULT_TRANSACTION_AWAITING_PATIENCE_IN_MILLISECONDS - - -@dataclass -class GetBlockArguments: - shard: Optional[int] = None - block_nonce: Optional[int] = None - block_hash: Optional[bytes] = None From 57167a2b5b68b5b6c92d5f5752a0ed962df1dffa Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 18 Dec 2024 23:27:47 +0200 Subject: [PATCH 17/17] small cookbook fix --- examples/v1.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/v1.ipynb b/examples/v1.ipynb index 1f997faa..63c7dce1 100644 --- a/examples/v1.ipynb +++ b/examples/v1.ipynb @@ -1900,7 +1900,7 @@ "source": [ "### Smart Contract queries\n", "\n", - "When querying a smart contract, a `view function` is called. That function does not modify the state of the contract, thus we don't need to send a transaction.\n", + "When querying a smart contract, a **view function** is called. That function does not modify the state of the contract, thus we don't need to send a transaction.\n", "\n", "To query a smart contract, we need to use the `SmartContractController`. Of course, we can use the contract's abi file to encode the arguments of the query, but also parse the result. In this example, we are going to use the [adder](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/adder) smart contract and we'll call the `getSum` endpoint." ]