From 924ca07c284e793c64c84c501a0cc2408b1dd7fe Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 29 Jan 2025 11:45:25 +0200 Subject: [PATCH 01/84] add precommit --- .flake8 | 2 +- .pre-commit-config.yaml | 35 +++++++++++++++++++++++++++++++++++ README.md | 5 +++++ requirements-dev.txt | 2 ++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .pre-commit-config.yaml diff --git a/.flake8 b/.flake8 index e44b8108..9983e283 100644 --- a/.flake8 +++ b/.flake8 @@ -1,2 +1,2 @@ [flake8] -ignore = E501 +extend-ignore = E501, E722, E203 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..d969e533 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,35 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/psf/black + rev: 24.10.0 + hooks: + - id: black + args: [--line-length=120] + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + name: isort (python) + args: ["--profile", "black", "--filter-files"] + - repo: https://github.com/PyCQA/flake8 + rev: 7.1.1 + hooks: + - id: flake8 + args: + - "--config=.flake8" + - repo: https://github.com/PyCQA/autoflake + rev: v2.3.1 + hooks: + - id: autoflake + args: + - --in-place + - --remove-all-unused-imports + - repo: https://github.com/RobertCraigie/pyright-python + rev: v1.1.392 + hooks: + - id: pyright diff --git a/README.md b/README.md index 89d55637..03722264 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,11 @@ Install development dependencies, as well: pip install -r ./requirements-dev.txt --upgrade ``` +Allow `pre-commit` to automatically run on `git commit`: +``` +pre-commit install +``` + Above, `requirements.txt` should mirror the **dependencies** section of `setup.py`. If using VSCode, restart it or follow these steps: diff --git a/requirements-dev.txt b/requirements-dev.txt index ce2c47ff..e4368771 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,3 +2,5 @@ pytest flake8 autopep8 pytest-mock +pre-commit +black From 65102d2c66fa033a14d774943ac8d97023f4c3e2 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 29 Jan 2025 11:46:15 +0200 Subject: [PATCH 02/84] format code using black --- .vscode/settings.json | 32 +- multiversx_sdk_cli/.vscode/settings.json | 18 - multiversx_sdk_cli/accounts.py | 59 +- multiversx_sdk_cli/cli.py | 26 +- multiversx_sdk_cli/cli_config.py | 22 +- multiversx_sdk_cli/cli_contracts.py | 503 ++++++++++--- multiversx_sdk_cli/cli_data.py | 28 +- multiversx_sdk_cli/cli_delegation.py | 316 ++++++-- multiversx_sdk_cli/cli_deps.py | 13 +- multiversx_sdk_cli/cli_dns.py | 95 ++- multiversx_sdk_cli/cli_faucet.py | 12 +- multiversx_sdk_cli/cli_ledger.py | 17 +- multiversx_sdk_cli/cli_localnet.py | 73 +- multiversx_sdk_cli/cli_output.py | 33 +- multiversx_sdk_cli/cli_shared.py | 278 ++++++-- multiversx_sdk_cli/cli_transactions.py | 64 +- multiversx_sdk_cli/cli_validators.py | 82 ++- multiversx_sdk_cli/cli_wallet.py | 126 ++-- multiversx_sdk_cli/config.py | 12 +- multiversx_sdk_cli/contract_verification.py | 35 +- multiversx_sdk_cli/contracts.py | 163 +++-- multiversx_sdk_cli/cosign_transaction.py | 4 +- .../delegation/staking_provider.py | 69 +- multiversx_sdk_cli/dependencies/__init__.py | 19 +- multiversx_sdk_cli/dependencies/install.py | 15 +- multiversx_sdk_cli/dependencies/modules.py | 80 ++- multiversx_sdk_cli/dependency_checker.py | 4 +- multiversx_sdk_cli/dns.py | 40 +- multiversx_sdk_cli/docker.py | 16 +- multiversx_sdk_cli/downloader.py | 18 +- multiversx_sdk_cli/errors.py | 12 +- multiversx_sdk_cli/interfaces.py | 1 + .../ledger/ledger_app_handler.py | 81 +-- multiversx_sdk_cli/ledger/ledger_functions.py | 11 +- multiversx_sdk_cli/localnet/config_default.py | 33 +- multiversx_sdk_cli/localnet/config_general.py | 12 +- .../localnet/config_networking.py | 26 +- multiversx_sdk_cli/localnet/config_part.py | 6 +- multiversx_sdk_cli/localnet/config_root.py | 24 +- .../localnet/config_sharding.py | 17 +- .../localnet/config_software.py | 27 +- multiversx_sdk_cli/localnet/constants.py | 4 +- multiversx_sdk_cli/localnet/genesis_json.py | 10 +- .../localnet/node_config_toml.py | 72 +- .../localnet/nodes_setup_json.py | 4 +- multiversx_sdk_cli/localnet/p2p_toml.py | 22 +- .../localnet/step_build_software.py | 7 +- multiversx_sdk_cli/localnet/step_config.py | 65 +- .../localnet/step_prerequisites.py | 6 +- multiversx_sdk_cli/localnet/step_start.py | 107 ++- multiversx_sdk_cli/myprocess.py | 16 +- multiversx_sdk_cli/native_auth_client.py | 16 +- multiversx_sdk_cli/projects/__init__.py | 19 +- multiversx_sdk_cli/projects/core.py | 6 +- multiversx_sdk_cli/projects/interfaces.py | 2 +- multiversx_sdk_cli/projects/migrations.py | 6 +- multiversx_sdk_cli/projects/project_base.py | 2 +- multiversx_sdk_cli/projects/project_rust.py | 38 +- .../projects/report/data/common.py | 43 +- .../projects/report/data/extracted_feature.py | 44 +- .../projects/report/data/folder_report.py | 23 +- .../projects/report/data/project_report.py | 34 +- .../projects/report/data/report.py | 33 +- .../projects/report/data/wasm_report.py | 27 +- .../projects/report/do_report.py | 27 +- .../projects/report/features/features.py | 4 +- .../projects/report/features/report_option.py | 2 +- .../report/features/twiggy_paths_check.py | 26 +- .../projects/report/format/change_type.py | 20 +- .../projects/report/report_creator.py | 48 +- multiversx_sdk_cli/projects/templates.py | 13 +- multiversx_sdk_cli/sign_verify.py | 7 +- multiversx_sdk_cli/simulation.py | 7 +- multiversx_sdk_cli/tests/conftest.py | 2 +- .../tests/local_verify_server.py | 14 +- .../tests/test_cli_contracts.py | 612 ++++++++++------ multiversx_sdk_cli/tests/test_cli_dns.py | 26 +- .../tests/test_cli_staking_provider.py | 672 ++++++++++++------ .../tests/test_cli_transactions.py | 339 ++++++--- .../tests/test_cli_validators.py | 329 ++++++--- multiversx_sdk_cli/tests/test_cli_wallet.py | 355 ++++++--- multiversx_sdk_cli/tests/test_contracts.py | 56 +- .../tests/test_native_auth_client.py | 79 +- .../tests/test_playground_proxy.py | 4 +- multiversx_sdk_cli/tests/test_proxy.py | 2 +- multiversx_sdk_cli/tests/test_sign.py | 23 +- multiversx_sdk_cli/tests/test_testnet.py | 14 +- .../tests/test_validators_core.py | 12 +- multiversx_sdk_cli/transactions.py | 46 +- multiversx_sdk_cli/utils.py | 13 +- multiversx_sdk_cli/validators/__init__.py | 46 +- multiversx_sdk_cli/validators/core.py | 42 +- .../validators/validators_file.py | 4 +- multiversx_sdk_cli/workstation.py | 2 +- 94 files changed, 3986 insertions(+), 1988 deletions(-) delete mode 100644 multiversx_sdk_cli/.vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 2a4f3afd..127cf7d3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,16 +1,20 @@ { - "python.testing.pytestArgs": [ - "multiversx_sdk_cli" - ], - "python.testing.pytestEnabled": true, - "editor.formatOnSave": true, - "[python]": { - "editor.defaultFormatter": "ms-python.autopep8" - }, - "python.formatting.provider": "autopep8", - "editor.codeActionsOnSave": { - "source.organizeImports": true - }, - "files.insertFinalNewline": true, - "python.languageServer": "Pylance" + "python.testing.pytestArgs": ["multiversx_sdk_cli"], + "python.testing.pytestEnabled": true, + "editor.formatOnSave": true, + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + }, + "files.insertFinalNewline": true, + "python.languageServer": "Pylance", + "files.autoSave": "onWindowChange", + "editor.fontSize": 13, + "files.trimTrailingWhitespace": true, + "github.gitProtocol": "ssh", + "python.analysis.autoFormatStrings": true, + "python.analysis.completeFunctionParens": true, + "black-formatter.args": ["--line-length=120"] } diff --git a/multiversx_sdk_cli/.vscode/settings.json b/multiversx_sdk_cli/.vscode/settings.json deleted file mode 100644 index 4ce5886a..00000000 --- a/multiversx_sdk_cli/.vscode/settings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "python.linting.pylintEnabled": false, - "python.linting.flake8Enabled": true, - "python.linting.enabled": true, - "python.testing.unittestArgs": [ - "-v", - "-s", - "./tests", - "-p", - "test_*.py" - ], - "python.testing.pytestEnabled": false, - "python.testing.unittestEnabled": true, - "python.testing.promptToConfigure": false, - "python.analysis.extraPaths": [ - ".." - ] -} diff --git a/multiversx_sdk_cli/accounts.py b/multiversx_sdk_cli/accounts.py index 8a0f9272..2bfa923a 100644 --- a/multiversx_sdk_cli/accounts.py +++ b/multiversx_sdk_cli/accounts.py @@ -2,25 +2,36 @@ from pathlib import Path from typing import Any, Optional, Protocol -from multiversx_sdk import (Address, Message, MessageComputer, Transaction, - TransactionComputer, UserSigner, AccountOnNetwork) +from multiversx_sdk import ( + AccountOnNetwork, + Address, + Message, + MessageComputer, + Transaction, + TransactionComputer, + UserSigner, +) from multiversx_sdk_cli.config import get_address_hrp -from multiversx_sdk_cli.interfaces import IAccount, IAddress +from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.ledger.config import compare_versions -from multiversx_sdk_cli.ledger.ledger_app_handler import \ - SIGN_USING_HASH_VERSION +from multiversx_sdk_cli.ledger.ledger_app_handler import SIGN_USING_HASH_VERSION from multiversx_sdk_cli.ledger.ledger_functions import ( - TX_HASH_SIGN_OPTIONS, TX_HASH_SIGN_VERSION, do_get_ledger_address, - do_get_ledger_version, do_sign_message_with_ledger, - do_sign_transaction_with_ledger) + TX_HASH_SIGN_OPTIONS, + TX_HASH_SIGN_VERSION, + do_get_ledger_address, + do_get_ledger_version, + do_sign_message_with_ledger, + do_sign_transaction_with_ledger, +) logger = logging.getLogger("accounts") - +# fmt: off class INetworkProvider(Protocol): def get_account(self, address: Address) -> AccountOnNetwork: ... +# fmt: on class AccountBase(IAccount): @@ -41,12 +52,14 @@ def sign_message(self, data: bytes) -> str: class Account(AccountBase): - def __init__(self, - address: Any = None, - pem_file: Optional[str] = None, - pem_index: int = 0, - key_file: str = "", - password: str = "") -> None: + def __init__( + self, + address: Any = None, + pem_file: Optional[str] = None, + pem_index: int = 0, + key_file: str = "", + password: str = "", + ) -> None: super().__init__(address) if pem_file: @@ -73,7 +86,9 @@ def sign_message(self, data: bytes) -> str: message_computer = MessageComputer() signature = self.signer.sign(message_computer.compute_bytes_for_signing(message)) - logger.debug(f"Account.sign_message(): raw_data_to_sign = {data.hex()}, message_data_to_sign = {message_computer.compute_bytes_for_signing(message).hex()}, signature = {signature.hex()}") + logger.debug( + f"Account.sign_message(): raw_data_to_sign = {data.hex()}, message_data_to_sign = {message_computer.compute_bytes_for_signing(message).hex()}, signature = {signature.hex()}" + ) return signature.hex() @@ -82,7 +97,9 @@ def __init__(self, account_index: int = 0, address_index: int = 0) -> None: super().__init__() self.account_index = account_index self.address_index = address_index - self.address = Address.new_from_bech32(do_get_ledger_address(account_index=account_index, address_index=address_index)) + self.address = Address.new_from_bech32( + do_get_ledger_address(account_index=account_index, address_index=address_index) + ) def sign_transaction(self, transaction: Transaction) -> str: ledger_version = do_get_ledger_version() @@ -99,7 +116,7 @@ def sign_transaction(self, transaction: Transaction) -> str: transaction_computer.compute_bytes_for_signing(transaction), account_index=self.account_index, address_index=self.address_index, - sign_using_hash=should_use_hash_signing + sign_using_hash=should_use_hash_signing, ) assert isinstance(signature, str) @@ -108,12 +125,14 @@ def sign_transaction(self, transaction: Transaction) -> str: def sign_message(self, data: bytes) -> str: message_length = len(data).to_bytes(4, byteorder="big") message_data_to_sign: bytes = message_length + data - logger.debug(f"LedgerAccount.sign_message(): raw_data_to_sign = {data.hex()}, message_data_to_sign = {message_data_to_sign.hex()}") + logger.debug( + f"LedgerAccount.sign_message(): raw_data_to_sign = {data.hex()}, message_data_to_sign = {message_data_to_sign.hex()}" + ) signature = do_sign_message_with_ledger( message_data_to_sign, account_index=self.account_index, - address_index=self.address_index + address_index=self.address_index, ) assert isinstance(signature, str) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 76fe95e1..a4eafff4 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -49,9 +49,18 @@ def _do_main(cli_args: List[str]): args = parser.parse_args(argv_with_config_args) if args.verbose: - logging.basicConfig(level="DEBUG", force=True, format='%(name)s: %(message)s', handlers=[RichHandler(show_time=False, rich_tracebacks=True)]) + logging.basicConfig( + level="DEBUG", + force=True, + format="%(name)s: %(message)s", + handlers=[RichHandler(show_time=False, rich_tracebacks=True)], + ) else: - logging.basicConfig(level="INFO", format='%(name)s: %(message)s', handlers=[RichHandler(show_time=False, rich_tracebacks=True)]) + logging.basicConfig( + level="INFO", + format="%(name)s: %(message)s", + handlers=[RichHandler(show_time=False, rich_tracebacks=True)], + ) verify_deprecated_entries_in_config_file() default_hrp = config.get_address_hrp() @@ -80,13 +89,18 @@ def setup_parser(args: List[str]): - https://docs.multiversx.com/sdk-and-tools/sdk-py - https://docs.multiversx.com/sdk-and-tools/sdk-py/mxpy-cli """, - formatter_class=argparse.RawDescriptionHelpFormatter + formatter_class=argparse.RawDescriptionHelpFormatter, ) parser._positionals.title = "COMMAND GROUPS" parser._optionals.title = "TOP-LEVEL OPTIONS" version = multiversx_sdk_cli.version.get_version() - parser.add_argument("-v", "--version", action="version", version=f"MultiversX Python CLI (mxpy) {version}") + parser.add_argument( + "-v", + "--version", + action="version", + version=f"MultiversX Python CLI (mxpy) {version}", + ) parser.add_argument("--verbose", action="store_true", default=False) subparsers = parser.add_subparsers() @@ -112,7 +126,7 @@ def setup_parser(args: List[str]): ---------------------- """ for choice, sub in subparsers.choices.items(): - parser.epilog += (f"{choice.ljust(30)} {sub.description}\n") + parser.epilog += f"{choice.ljust(30)} {sub.description}\n" return parser @@ -127,7 +141,7 @@ def verify_deprecated_entries_in_config_file(): for entry in deprecated_keys: message += f"-> {entry} \n" - ux.show_warning(message.rstrip('\n')) + ux.show_warning(message.rstrip("\n")) if __name__ == "__main__": diff --git a/multiversx_sdk_cli/cli_config.py b/multiversx_sdk_cli/cli_config.py index 4e03879d..3d536af0 100644 --- a/multiversx_sdk_cli/cli_config.py +++ b/multiversx_sdk_cli/cli_config.py @@ -14,7 +14,12 @@ def setup_parser(subparsers: Any) -> Any: subparsers = parser.add_subparsers() sub = cli_shared.add_command_subparser(subparsers, "config", "dump", "Dumps configuration.") - sub.add_argument('--defaults', required=False, help='dump defaults instead of local config', action='store_true') + sub.add_argument( + "--defaults", + required=False, + help="dump defaults instead of local config", + action="store_true", + ) sub.set_defaults(func=dump) sub = cli_shared.add_command_subparser(subparsers, "config", "get", "Gets a configuration value.") @@ -32,7 +37,11 @@ def setup_parser(subparsers: Any) -> Any: sub = cli_shared.add_command_subparser(subparsers, "config", "new", "Creates a new configuration.") _add_name_arg(sub) - sub.add_argument("--template", required=False, help="template from which to create the new config") + sub.add_argument( + "--template", + required=False, + help="template from which to create the new config", + ) sub.set_defaults(func=new_config) sub = cli_shared.add_command_subparser(subparsers, "config", "switch", "Switch to a different config") @@ -42,7 +51,12 @@ def setup_parser(subparsers: Any) -> Any: sub = cli_shared.add_command_subparser(subparsers, "config", "list", "List available configs") sub.set_defaults(func=list_configs) - sub = cli_shared.add_command_subparser(subparsers, "config", "reset", "Deletes the config file. Default config will be used.") + sub = cli_shared.add_command_subparser( + subparsers, + "config", + "reset", + "Deletes the config file. Default config will be used.", + ) sub.set_defaults(func=delete_config) parser.epilog = cli_shared.build_group_epilog(subparsers) @@ -93,7 +107,7 @@ def switch_config(args: Any): def list_configs(args: Any): data = config.read_file() - configurations = data.get('configurations', {}) + configurations = data.get("configurations", {}) for config_name in configurations.keys(): if config_name == data.get("active", "default"): config_name += "*" diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 9516bcf4..c663d41f 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -4,16 +4,20 @@ from pathlib import Path from typing import Any, List, Tuple -from multiversx_sdk import (Address, AddressComputer, ProxyNetworkProvider, - Transaction, TransactionsFactoryConfig) +from multiversx_sdk import ( + Address, + AddressComputer, + ProxyNetworkProvider, + Transaction, + TransactionsFactoryConfig, +) from multiversx_sdk.abi import Abi from multiversx_sdk_cli import cli_shared, projects, utils from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS -from multiversx_sdk_cli.contract_verification import \ - trigger_contract_verification +from multiversx_sdk_cli.contract_verification import trigger_contract_verification from multiversx_sdk_cli.contracts import SmartContract from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.dependency_checker import check_if_rust_is_installed @@ -28,52 +32,126 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: - parser = cli_shared.add_group_subparser(subparsers, "contract", "Build, deploy, upgrade and interact with Smart Contracts") + parser = cli_shared.add_group_subparser( + subparsers, + "contract", + "Build, deploy, upgrade and interact with Smart Contracts", + ) subparsers = parser.add_subparsers() - sub = cli_shared.add_command_subparser(subparsers, "contract", "new", - "Create a new Smart Contract project based on a template.") - sub.add_argument("--name", help="The name of the contract. If missing, the name of the template will be used.") + sub = cli_shared.add_command_subparser( + subparsers, + "contract", + "new", + "Create a new Smart Contract project based on a template.", + ) + sub.add_argument( + "--name", + help="The name of the contract. If missing, the name of the template will be used.", + ) sub.add_argument("--template", required=True, help="the template to use") sub.add_argument("--tag", help="the framework version on which the contract should be created") - sub.add_argument("--path", type=str, default=os.getcwd(), - help="the parent directory of the project (default: current directory)") + sub.add_argument( + "--path", + type=str, + default=os.getcwd(), + help="the parent directory of the project (default: current directory)", + ) sub.set_defaults(func=create) - sub = cli_shared.add_command_subparser(subparsers, "contract", "templates", - "List the available Smart Contract templates.") + sub = cli_shared.add_command_subparser( + subparsers, + "contract", + "templates", + "List the available Smart Contract templates.", + ) sub.add_argument("--tag", help="the sc-meta framework version referred to") sub.set_defaults(func=list_templates) - sub = cli_shared.add_command_subparser(subparsers, "contract", "build", - "Build a Smart Contract project.") + sub = cli_shared.add_command_subparser(subparsers, "contract", "build", "Build a Smart Contract project.") _add_build_options_sc_meta(sub) sub.set_defaults(func=build) sub = cli_shared.add_command_subparser(subparsers, "contract", "clean", "Clean a Smart Contract project.") - sub.add_argument("--path", default=os.getcwd(), help="the project directory (default: current directory)") + sub.add_argument( + "--path", + default=os.getcwd(), + help="the project directory (default: current directory)", + ) sub.set_defaults(func=clean) sub = cli_shared.add_command_subparser(subparsers, "contract", "test", "Run tests.") - sub.add_argument("--path", default=os.getcwd(), - help="the directory of the contract (default: %(default)s)") - sub.add_argument("--go", action="store_true", - help="this arg runs rust and go tests (default: false)") - sub.add_argument("--scen", action="store_true", help="this arg runs scenarios (default: false). If `--scen` and `--go` are both specified, scen overrides the go argument") - sub.add_argument("--nocapture", action="store_true", help="this arg prints the entire output of the vm (default: false)") + sub.add_argument( + "--path", + default=os.getcwd(), + help="the directory of the contract (default: %(default)s)", + ) + sub.add_argument( + "--go", + action="store_true", + help="this arg runs rust and go tests (default: false)", + ) + sub.add_argument( + "--scen", + action="store_true", + help="this arg runs scenarios (default: false). If `--scen` and `--go` are both specified, scen overrides the go argument", + ) + sub.add_argument( + "--nocapture", + action="store_true", + help="this arg prints the entire output of the vm (default: false)", + ) sub.set_defaults(func=run_tests) - sub = cli_shared.add_command_subparser(subparsers, "contract", "report", "Print a detailed report of the smart contracts.") - sub.add_argument("--skip-build", action="store_true", default=False, help="skips the step of building of the wasm contracts") - sub.add_argument("--skip-twiggy", action="store_true", default=False, help="skips the steps of building the debug wasm files and running twiggy") - sub.add_argument("--output-format", type=str, default="text-markdown", choices=["github-markdown", "text-markdown", "json"], help="report output format (default: %(default)s)") - sub.add_argument("--output-file", type=Path, help="if specified, the output is written to a file, otherwise it's written to the standard output") - sub.add_argument("--compare", type=Path, nargs='+', metavar=("report-1.json", "report-2.json"), help="create a comparison from two or more reports") + sub = cli_shared.add_command_subparser( + subparsers, + "contract", + "report", + "Print a detailed report of the smart contracts.", + ) + sub.add_argument( + "--skip-build", + action="store_true", + default=False, + help="skips the step of building of the wasm contracts", + ) + sub.add_argument( + "--skip-twiggy", + action="store_true", + default=False, + help="skips the steps of building the debug wasm files and running twiggy", + ) + sub.add_argument( + "--output-format", + type=str, + default="text-markdown", + choices=["github-markdown", "text-markdown", "json"], + help="report output format (default: %(default)s)", + ) + sub.add_argument( + "--output-file", + type=Path, + help="if specified, the output is written to a file, otherwise it's written to the standard output", + ) + sub.add_argument( + "--compare", + type=Path, + nargs="+", + metavar=("report-1.json", "report-2.json"), + help="create a comparison from two or more reports", + ) _add_build_options_sc_meta(sub) sub.set_defaults(func=do_report) - output_description = CLIOutputBuilder.describe(with_contract=True, with_transaction_on_network=True, with_simulation=True) - sub = cli_shared.add_command_subparser(subparsers, "contract", "deploy", f"Deploy a Smart Contract.{output_description}") + output_description = CLIOutputBuilder.describe( + with_contract=True, with_transaction_on_network=True, with_simulation=True + ) + sub = cli_shared.add_command_subparser( + subparsers, + "contract", + "deploy", + f"Deploy a Smart Contract.{output_description}", + ) _add_bytecode_arg(sub) _add_contract_abi_arg(sub) _add_metadata_arg(sub) @@ -82,17 +160,28 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) _add_arguments_arg(sub) - sub.add_argument("--wait-result", action="store_true", default=False, - help="signal to wait for the transaction result - only valid if --send is set") - sub.add_argument("--timeout", default=100, help="max num of seconds to wait for result" - " - only valid if --wait-result is set") + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) sub.set_defaults(func=deploy) - sub = cli_shared.add_command_subparser(subparsers, "contract", "call", - f"Interact with a Smart Contract (execute function).{output_description}") + sub = cli_shared.add_command_subparser( + subparsers, + "contract", + "call", + f"Interact with a Smart Contract (execute function).{output_description}", + ) _add_contract_arg(sub) _add_contract_abi_arg(sub) cli_shared.add_outfile_arg(sub) @@ -102,17 +191,28 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: _add_function_arg(sub) _add_arguments_arg(sub) cli_shared.add_token_transfers_args(sub) - sub.add_argument("--wait-result", action="store_true", default=False, - help="signal to wait for the transaction result - only valid if --send is set") - sub.add_argument("--timeout", default=100, help="max num of seconds to wait for result" - " - only valid if --wait-result is set") + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) cli_shared.add_broadcast_args(sub, relay=True) cli_shared.add_guardian_wallet_args(args, sub) sub.set_defaults(func=call) - sub = cli_shared.add_command_subparser(subparsers, "contract", "upgrade", - f"Upgrade a previously-deployed Smart Contract.{output_description}") + sub = cli_shared.add_command_subparser( + subparsers, + "contract", + "upgrade", + f"Upgrade a previously-deployed Smart Contract.{output_description}", + ) _add_contract_arg(sub) _add_contract_abi_arg(sub) cli_shared.add_outfile_arg(sub) @@ -122,17 +222,25 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) _add_arguments_arg(sub) - sub.add_argument("--wait-result", action="store_true", default=False, - help="signal to wait for the transaction result - only valid if --send is set") - sub.add_argument("--timeout", default=100, help="max num of seconds to wait for result" - " - only valid if --wait-result is set") + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) sub.set_defaults(func=upgrade) - sub = cli_shared.add_command_subparser(subparsers, "contract", "query", - "Query a Smart Contract (call a pure function)") + sub = cli_shared.add_command_subparser( + subparsers, "contract", "query", "Query a Smart Contract (call a pure function)" + ) _add_contract_arg(sub) _add_contract_abi_arg(sub) cli_shared.add_proxy_arg(sub) @@ -140,12 +248,17 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: _add_arguments_arg(sub) sub.set_defaults(func=query) - sub = cli_shared.add_command_subparser(subparsers, "contract", "verify", - "Verify the authenticity of the code of a deployed Smart Contract", - ) + sub = cli_shared.add_command_subparser( + subparsers, + "contract", + "verify", + "Verify the authenticity of the code of a deployed Smart Contract", + ) sub.add_argument( - "--packaged-src", required=True, help="JSON file containing the source code of the contract" + "--packaged-src", + required=True, + help="JSON file containing the source code of the contract", ) _add_contract_arg(sub) @@ -155,21 +268,42 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: help="the url of the service that validates the contract", ) sub.add_argument("--docker-image", required=True, help="the docker image used for the build") - sub.add_argument("--contract-variant", required=False, default=None, help="in case of a multicontract, specify the contract variant you want to verify") + sub.add_argument( + "--contract-variant", + required=False, + default=None, + help="in case of a multicontract, specify the contract variant you want to verify", + ) cli_shared.add_wallet_args(args, sub) sub.set_defaults(func=verify) - sub = cli_shared.add_command_subparser(subparsers, "contract", "reproducible-build", - "Build a Smart Contract and get the same output as a previously built Smart Contract") + sub = cli_shared.add_command_subparser( + subparsers, + "contract", + "reproducible-build", + "Build a Smart Contract and get the same output as a previously built Smart Contract", + ) _add_project_arg(sub) _add_build_options_args(sub) - sub.add_argument("--docker-image", required=True, type=str, - help="the docker image tag used to build the contract") - sub.add_argument("--contract", type=str, help="contract to build (contract name, as found in Cargo.toml)") + sub.add_argument( + "--docker-image", + required=True, + type=str, + help="the docker image tag used to build the contract", + ) + sub.add_argument( + "--contract", + type=str, + help="contract to build (contract name, as found in Cargo.toml)", + ) sub.add_argument("--no-docker-interactive", action="store_true", default=False) sub.add_argument("--no-docker-tty", action="store_true", default=False) - sub.add_argument("--no-default-platform", action="store_true", default=False, - help="do not set DOCKER_DEFAULT_PLATFORM environment variable to 'linux/amd64'") + sub.add_argument( + "--no-default-platform", + action="store_true", + default=False, + help="do not set DOCKER_DEFAULT_PLATFORM environment variable to 'linux/amd64'", + ) sub.set_defaults(func=do_reproducible_build) parser.epilog = cli_shared.build_group_epilog(subparsers) @@ -177,51 +311,153 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: def _add_project_arg(sub: Any): - sub.add_argument("project", nargs='?', default=os.getcwd(), - help="the project directory (default: current directory)") + sub.add_argument( + "project", + nargs="?", + default=os.getcwd(), + help="the project directory (default: current directory)", + ) def _add_build_options_sc_meta(sub: Any): - sub.add_argument("--path", default=os.getcwd(), help="the project directory (default: current directory)") - sub.add_argument("--no-wasm-opt", action="store_true", default=False, - help="do not optimize wasm files after the build (default: %(default)s)") - sub.add_argument("--wasm-symbols", action="store_true", default=False, - help="for rust projects, does not strip the symbols from the wasm output. Useful for analysing the bytecode. Creates larger wasm files. Avoid in production (default: %(default)s)") - sub.add_argument("--wasm-name", type=str, - help="for rust projects, optionally specify the name of the wasm bytecode output file") - sub.add_argument("--wasm-suffix", type=str, - help="for rust projects, optionally specify the suffix of the wasm bytecode output file") - sub.add_argument("--target-dir", type=str, help="for rust projects, forward the parameter to Cargo") - sub.add_argument("--wat", action="store_true", help="also generate a WAT file when building", default=False) - sub.add_argument("--mir", action="store_true", help="also emit MIR files when building", default=False) - sub.add_argument("--llvm-ir", action="store_true", help="also emit LL (LLVM) files when building", default=False) + sub.add_argument( + "--path", + default=os.getcwd(), + help="the project directory (default: current directory)", + ) + sub.add_argument( + "--no-wasm-opt", + action="store_true", + default=False, + help="do not optimize wasm files after the build (default: %(default)s)", + ) + sub.add_argument( + "--wasm-symbols", + action="store_true", + default=False, + help="for rust projects, does not strip the symbols from the wasm output. Useful for analysing the bytecode. Creates larger wasm files. Avoid in production (default: %(default)s)", + ) + sub.add_argument( + "--wasm-name", + type=str, + help="for rust projects, optionally specify the name of the wasm bytecode output file", + ) + sub.add_argument( + "--wasm-suffix", + type=str, + help="for rust projects, optionally specify the suffix of the wasm bytecode output file", + ) + sub.add_argument( + "--target-dir", + type=str, + help="for rust projects, forward the parameter to Cargo", + ) + sub.add_argument( + "--wat", + action="store_true", + help="also generate a WAT file when building", + default=False, + ) + sub.add_argument( + "--mir", + action="store_true", + help="also emit MIR files when building", + default=False, + ) + sub.add_argument( + "--llvm-ir", + action="store_true", + help="also emit LL (LLVM) files when building", + default=False, + ) sub.add_argument("--ignore", help="ignore all directories with these names. [default: target]") - sub.add_argument("--no-imports", action="store_true", default=False, help="skips extracting the EI imports after building the contracts") - sub.add_argument("--no-abi-git-version", action="store_true", default=False, help="skips loading the Git version into the ABI") - sub.add_argument("--twiggy-top", action="store_true", default=False, help="generate a twiggy top report after building") - sub.add_argument("--twiggy-paths", action="store_true", default=False, help="generate a twiggy paths report after building") - sub.add_argument("--twiggy-monos", action="store_true", default=False, help="generate a twiggy monos report after building") - sub.add_argument("--twiggy-dominators", action="store_true", default=False, help="generate a twiggy dominators report after building") + sub.add_argument( + "--no-imports", + action="store_true", + default=False, + help="skips extracting the EI imports after building the contracts", + ) + sub.add_argument( + "--no-abi-git-version", + action="store_true", + default=False, + help="skips loading the Git version into the ABI", + ) + sub.add_argument( + "--twiggy-top", + action="store_true", + default=False, + help="generate a twiggy top report after building", + ) + sub.add_argument( + "--twiggy-paths", + action="store_true", + default=False, + help="generate a twiggy paths report after building", + ) + sub.add_argument( + "--twiggy-monos", + action="store_true", + default=False, + help="generate a twiggy monos report after building", + ) + sub.add_argument( + "--twiggy-dominators", + action="store_true", + default=False, + help="generate a twiggy dominators report after building", + ) def _add_build_options_args(sub: Any): - sub.add_argument("--debug", action="store_true", default=False, help="set debug flag (default: %(default)s)") - sub.add_argument("--no-optimization", action="store_true", default=False, - help="bypass optimizations (for clang) (default: %(default)s)") - sub.add_argument("--no-wasm-opt", action="store_true", default=False, - help="do not optimize wasm files after the build (default: %(default)s)") - sub.add_argument("--cargo-target-dir", type=str, help="for rust projects, forward the parameter to Cargo") - sub.add_argument("--wasm-symbols", action="store_true", default=False, - help="for rust projects, does not strip the symbols from the wasm output. Useful for analysing the bytecode. Creates larger wasm files. Avoid in production (default: %(default)s)") - sub.add_argument("--wasm-name", type=str, - help="for rust projects, optionally specify the name of the wasm bytecode output file") - sub.add_argument("--wasm-suffix", type=str, - help="for rust projects, optionally specify the suffix of the wasm bytecode output file") + sub.add_argument( + "--debug", + action="store_true", + default=False, + help="set debug flag (default: %(default)s)", + ) + sub.add_argument( + "--no-optimization", + action="store_true", + default=False, + help="bypass optimizations (for clang) (default: %(default)s)", + ) + sub.add_argument( + "--no-wasm-opt", + action="store_true", + default=False, + help="do not optimize wasm files after the build (default: %(default)s)", + ) + sub.add_argument( + "--cargo-target-dir", + type=str, + help="for rust projects, forward the parameter to Cargo", + ) + sub.add_argument( + "--wasm-symbols", + action="store_true", + default=False, + help="for rust projects, does not strip the symbols from the wasm output. Useful for analysing the bytecode. Creates larger wasm files. Avoid in production (default: %(default)s)", + ) + sub.add_argument( + "--wasm-name", + type=str, + help="for rust projects, optionally specify the name of the wasm bytecode output file", + ) + sub.add_argument( + "--wasm-suffix", + type=str, + help="for rust projects, optionally specify the suffix of the wasm bytecode output file", + ) def _add_bytecode_arg(sub: Any): - sub.add_argument("--bytecode", type=str, required=True, - help="the file containing the WASM bytecode") + sub.add_argument( + "--bytecode", + type=str, + required=True, + help="the file containing the WASM bytecode", + ) def _add_contract_arg(sub: Any): @@ -237,22 +473,45 @@ def _add_function_arg(sub: Any): def _add_arguments_arg(sub: Any): - sub.add_argument("--arguments", nargs='+', - help="arguments for the contract transaction, as [number, bech32-address, ascii string, " - "boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 0xabba str:TOK-a1c2ef true erd1[..]") - sub.add_argument("--arguments-file", type=str, help="a json file containing the arguments. ONLY if abi file is provided. " - "E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }]") + sub.add_argument( + "--arguments", + nargs="+", + help="arguments for the contract transaction, as [number, bech32-address, ascii string, " + "boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 0xabba str:TOK-a1c2ef true erd1[..]", + ) + sub.add_argument( + "--arguments-file", + type=str, + help="a json file containing the arguments. ONLY if abi file is provided. " + "E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }]", + ) def _add_metadata_arg(sub: Any): - sub.add_argument("--metadata-not-upgradeable", dest="metadata_upgradeable", action="store_false", - help="‼ mark the contract as NOT upgradeable (default: upgradeable)") - sub.add_argument("--metadata-not-readable", dest="metadata_readable", action="store_false", - help="‼ mark the contract as NOT readable (default: readable)") - sub.add_argument("--metadata-payable", dest="metadata_payable", action="store_true", - help="‼ mark the contract as payable (default: not payable)") - sub.add_argument("--metadata-payable-by-sc", dest="metadata_payable_by_sc", action="store_true", - help="‼ mark the contract as payable by SC (default: not payable by SC)") + sub.add_argument( + "--metadata-not-upgradeable", + dest="metadata_upgradeable", + action="store_false", + help="‼ mark the contract as NOT upgradeable (default: upgradeable)", + ) + sub.add_argument( + "--metadata-not-readable", + dest="metadata_readable", + action="store_false", + help="‼ mark the contract as NOT readable (default: readable)", + ) + sub.add_argument( + "--metadata-payable", + dest="metadata_payable", + action="store_true", + help="‼ mark the contract as payable (default: not payable)", + ) + sub.add_argument( + "--metadata-payable-by-sc", + dest="metadata_payable_by_sc", + action="store_true", + help="‼ mark the contract as payable by SC (default: not payable by SC)", + ) sub.set_defaults(metadata_upgradeable=True, metadata_payable=False) @@ -294,7 +553,9 @@ def build(args: Any): for project in project_paths: projects.build_project(project, arg_list) - show_warning("The primary tool for building smart contracts is `sc-meta`. Try using the `sc-meta all build` command.") + show_warning( + "The primary tool for building smart contracts is `sc-meta`. Try using the `sc-meta all build` command." + ) def do_report(args: Any): @@ -340,7 +601,8 @@ def deploy(args: Any): nonce=int(args.nonce), version=int(args.version), options=int(args.options), - guardian=args.guardian) + guardian=args.guardian, + ) tx = _sign_guarded_tx(args, tx) address_computer = AddressComputer(NUMBER_OF_SHARDS) @@ -394,7 +656,8 @@ def call(args: Any): nonce=int(args.nonce), version=int(args.version), options=int(args.options), - guardian=args.guardian) + guardian=args.guardian, + ) tx = _sign_guarded_tx(args, tx) _send_or_simulate(tx, contract_address, args) @@ -430,7 +693,8 @@ def upgrade(args: Any): nonce=int(args.nonce), version=int(args.version), options=int(args.options), - guardian=args.guardian) + guardian=args.guardian, + ) tx = _sign_guarded_tx(args, tx) _send_or_simulate(tx, contract_address, args) @@ -459,7 +723,7 @@ def query(args: Any): proxy=proxy, function=function, arguments=arguments, - should_prepare_args=should_prepare_args + should_prepare_args=should_prepare_args, ) utils.dump_out_json(result) @@ -496,9 +760,7 @@ def verify(args: Any) -> None: docker_image = args.docker_image contract_variant = args.contract_variant - trigger_contract_verification( - packaged_src, owner, contract, verifier_url, docker_image, contract_variant - ) + trigger_contract_verification(packaged_src, owner, contract, verifier_url, docker_image, contract_variant) logger.info("Contract verification request completed!") @@ -523,7 +785,16 @@ def do_reproducible_build(args: Any): raise DockerMissingError() logger.info("Starting the docker run...") - run_docker(docker_image, project_path, contract_path, output_path, no_wasm_opt, docker_interactive, docker_tty, no_default_platform) + run_docker( + docker_image, + project_path, + contract_path, + output_path, + no_wasm_opt, + docker_interactive, + docker_tty, + no_default_platform, + ) logger.info("Docker build ran successfully!") logger.info(f"Inspect summary of generated artifacts here: {artifacts_path}") diff --git a/multiversx_sdk_cli/cli_data.py b/multiversx_sdk_cli/cli_data.py index fc62fc0b..6b13f6fa 100644 --- a/multiversx_sdk_cli/cli_data.py +++ b/multiversx_sdk_cli/cli_data.py @@ -16,20 +16,36 @@ def setup_parser(subparsers: Any) -> Any: sub = cli_shared.add_command_subparser(subparsers, "data", "parse", "Parses values from a given file") sub.add_argument("--file", required=True, help="path of the file to parse") - sub.add_argument("--expression", required=True, help="the Python-Dictionary expression to evaluate in order to extract the data") + sub.add_argument( + "--expression", + required=True, + help="the Python-Dictionary expression to evaluate in order to extract the data", + ) sub.set_defaults(func=parse) sub = cli_shared.add_command_subparser(subparsers, "data", "store", "Stores a key-value pair within a partition") sub.add_argument("--key", required=True, help="the key") sub.add_argument("--value", required=True, help="the value to save") sub.add_argument("--partition", default="*", help="the storage partition (default: %(default)s)") - sub.add_argument("--use-global", action="store_true", default=False, help="use the global storage (default: %(default)s)") + sub.add_argument( + "--use-global", + action="store_true", + default=False, + help="use the global storage (default: %(default)s)", + ) sub.set_defaults(func=store) - sub = cli_shared.add_command_subparser(subparsers, "data", "load", "Loads a key-value pair from a storage partition") + sub = cli_shared.add_command_subparser( + subparsers, "data", "load", "Loads a key-value pair from a storage partition" + ) sub.add_argument("--key", required=True, help="the key") sub.add_argument("--partition", default="*", help="the storage partition (default: %(default)s)") - sub.add_argument("--use-global", action="store_true", default=False, help="use the global storage (default: %(default)s)") + sub.add_argument( + "--use-global", + action="store_true", + default=False, + help="use the global storage (default: %(default)s)", + ) sub.set_defaults(func=load) parser.epilog = cli_shared.build_group_epilog(subparsers) @@ -48,9 +64,7 @@ def parse(args: Any): raise errors.BadUsage(f"File isn't parsable: {file}") try: - result = eval(expression, { - "data": data - }) + result = eval(expression, {"data": data}) except KeyError: result = "" diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index cc6dcaa2..2e0a35f1 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -12,167 +12,339 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: subparsers = parser.add_subparsers() # create new delegation contract - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "create-new-delegation-contract", - "Create a new delegation system smart contract, transferred value must be " - "greater than baseIssuingCost + min deposit value") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "create-new-delegation-contract", + "Create a new delegation system smart contract, transferred value must be " + "greater than baseIssuingCost + min deposit value", + ) _add_common_arguments(args, sub) - sub.add_argument("--total-delegation-cap", required=True, help="the total delegation contract capacity") + sub.add_argument( + "--total-delegation-cap", + required=True, + help="the total delegation contract capacity", + ) sub.add_argument("--service-fee", required=True, help="the delegation contract service fee") sub.set_defaults(func=do_create_delegation_contract) # get contract address - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "get-contract-address", - "Get create contract address by transaction hash") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "get-contract-address", + "Get create contract address by transaction hash", + ) sub.add_argument("--create-tx-hash", required=True, help="the hash") sub.add_argument("--sender", required=False, help="the sender address") cli_shared.add_proxy_arg(sub) sub.set_defaults(func=get_contract_address_by_deploy_tx_hash) # add a new node - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "add-nodes", - "Add new nodes must be called by the contract owner") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "add-nodes", + "Add new nodes must be called by the contract owner", + ) sub.add_argument("--validators-file", required=True, help="a JSON file describing the Nodes") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=add_new_nodes) # remove nodes - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "remove-nodes", - "Remove nodes must be called by the contract owner") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "remove-nodes", + "Remove nodes must be called by the contract owner", + ) sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") sub.add_argument("--validators-file", help="a JSON file describing the Nodes") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=remove_nodes) # stake nodes - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "stake-nodes", - "Stake nodes must be called by the contract owner") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "stake-nodes", + "Stake nodes must be called by the contract owner", + ) sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") sub.add_argument("--validators-file", help="a JSON file describing the Nodes") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=stake_nodes) # unbond nodes - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "unbond-nodes", - "Unbond nodes must be called by the contract owner") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "unbond-nodes", + "Unbond nodes must be called by the contract owner", + ) sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") sub.add_argument("--validators-file", help="a JSON file describing the Nodes") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=unbond_nodes) # unstake nodes - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "unstake-nodes", - "Unstake nodes must be called by the contract owner") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "unstake-nodes", + "Unstake nodes must be called by the contract owner", + ) sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") sub.add_argument("--validators-file", help="a JSON file describing the Nodes") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=unstake_nodes) # unjail nodes - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "unjail-nodes", - "Unjail nodes must be called by the contract owner") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "unjail-nodes", + "Unjail nodes must be called by the contract owner", + ) sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") sub.add_argument("--validators-file", help="a JSON file describing the Nodes") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=unjail_nodes) # delegate - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "delegate", - "Delegate funds to a delegation contract") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "delegate", + "Delegate funds to a delegation contract", + ) + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=delegate) # claim rewards - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "claim-rewards", - "Claim the rewards earned for delegating") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "claim-rewards", + "Claim the rewards earned for delegating", + ) + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=claim_rewards) # redelegate rewards - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "redelegate-rewards", - "Redelegate the rewards earned for delegating") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "redelegate-rewards", + "Redelegate the rewards earned for delegating", + ) + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=redelegate_rewards) # undelegate - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "undelegate", - "Undelegate funds from a delegation contract") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "undelegate", + "Undelegate funds from a delegation contract", + ) + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=undelegate) # withdraw - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "withdraw", - "Withdraw funds from a delegation contract") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "withdraw", + "Withdraw funds from a delegation contract", + ) + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=withdraw) # change service fee - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "change-service-fee", - "Change service fee must be called by the contract owner") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "change-service-fee", + "Change service fee must be called by the contract owner", + ) sub.add_argument("--service-fee", required=True, help="new service fee value") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=change_service_fee) # modify total delegation cap - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "modify-delegation-cap", - "Modify delegation cap must be called by the contract owner") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "modify-delegation-cap", + "Modify delegation cap must be called by the contract owner", + ) sub.add_argument("--delegation-cap", required=True, help="new delegation contract capacity") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=modify_delegation_cap) # set automatic activation - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "automatic-activation", - "Automatic activation must be called by the contract owner") - - sub.add_argument("--set", action="store_true", required=not (utils.is_arg_present(args, "--unset")), - help="set automatic activation True") - sub.add_argument("--unset", action="store_true", required=not (utils.is_arg_present(args, "--set")), - help="set automatic activation False") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "automatic-activation", + "Automatic activation must be called by the contract owner", + ) + + sub.add_argument( + "--set", + action="store_true", + required=not (utils.is_arg_present(args, "--unset")), + help="set automatic activation True", + ) + sub.add_argument( + "--unset", + action="store_true", + required=not (utils.is_arg_present(args, "--set")), + help="set automatic activation False", + ) + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=automatic_activation) # set redelegate cap - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "redelegate-cap", - "Redelegate cap must be called by the contract owner") - - sub.add_argument("--set", action="store_true", required=not (utils.is_arg_present(args, "--unset")), - help="set redelegate cap True") - sub.add_argument("--unset", action="store_true", required=not (utils.is_arg_present(args, "--set")), - help="set redelegate cap False") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "redelegate-cap", + "Redelegate cap must be called by the contract owner", + ) + + sub.add_argument( + "--set", + action="store_true", + required=not (utils.is_arg_present(args, "--unset")), + help="set redelegate cap True", + ) + sub.add_argument( + "--unset", + action="store_true", + required=not (utils.is_arg_present(args, "--set")), + help="set redelegate cap False", + ) + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=redelegate_cap) # set metadata - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "set-metadata", - "Set metadata must be called by the contract owner") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "set-metadata", + "Set metadata must be called by the contract owner", + ) sub.add_argument("--name", required=True, help="name field in staking provider metadata") sub.add_argument("--website", required=True, help="website field in staking provider metadata") - sub.add_argument("--identifier", required=True, help="identifier field in staking provider metadata") - sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") + sub.add_argument( + "--identifier", + required=True, + help="identifier field in staking provider metadata", + ) + sub.add_argument( + "--delegation-contract", + required=True, + help="address of the delegation contract", + ) _add_common_arguments(args, sub) sub.set_defaults(func=set_metadata) # convert validator to delegation contract - sub = cli_shared.add_command_subparser(subparsers, "staking-provider", "make-delegation-contract-from-validator", - "Create a delegation contract from validator data. Must be called by the node operator") - - sub.add_argument("--max-cap", required=True, help="total delegation cap in EGLD, fully denominated. Use value 0 for uncapped") - sub.add_argument("--fee", required=True, help="service fee as hundredths of percents. (e.g. a service fee of 37.45 percent is expressed by the integer 3745)") + sub = cli_shared.add_command_subparser( + subparsers, + "staking-provider", + "make-delegation-contract-from-validator", + "Create a delegation contract from validator data. Must be called by the node operator", + ) + + sub.add_argument( + "--max-cap", + required=True, + help="total delegation cap in EGLD, fully denominated. Use value 0 for uncapped", + ) + sub.add_argument( + "--fee", + required=True, + help="service fee as hundredths of percents. (e.g. a service fee of 37.45 percent is expressed by the integer 3745)", + ) _add_common_arguments(args, sub) sub.set_defaults(func=make_new_contract_from_validator_data) @@ -219,7 +391,9 @@ def get_contract_address_by_deploy_tx_hash(args: Any): contract_address = transaction_events[0].address print(contract_address.to_bech32()) else: - raise errors.ProgrammingError("Tx has more than one event. Make sure it's a staking provider SC Deploy transaction.") + raise errors.ProgrammingError( + "Tx has more than one event. Make sure it's a staking provider SC Deploy transaction." + ) def add_new_nodes(args: Any): diff --git a/multiversx_sdk_cli/cli_deps.py b/multiversx_sdk_cli/cli_deps.py index bf93c8e0..4c3c31ad 100644 --- a/multiversx_sdk_cli/cli_deps.py +++ b/multiversx_sdk_cli/cli_deps.py @@ -12,11 +12,18 @@ def setup_parser(subparsers: Any) -> Any: parser = cli_shared.add_group_subparser(subparsers, "deps", "Manage dependencies or multiversx-sdk modules") subparsers = parser.add_subparsers() - choices = ['all'] + list(get_deps_dict().keys()) + choices = ["all"] + list(get_deps_dict().keys()) - sub = cli_shared.add_command_subparser(subparsers, "deps", "install", "Install dependencies or multiversx-sdk modules.") + sub = cli_shared.add_command_subparser( + subparsers, "deps", "install", "Install dependencies or multiversx-sdk modules." + ) sub.add_argument("name", choices=choices, help="the dependency to install") - sub.add_argument("--overwrite", action="store_true", default=False, help="whether to overwrite an existing installation") + sub.add_argument( + "--overwrite", + action="store_true", + default=False, + help="whether to overwrite an existing installation", + ) sub.set_defaults(func=install) sub = cli_shared.add_command_subparser(subparsers, "deps", "check", "Check whether a dependency is installed.") diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index dbbc9c9b..d26c1dc8 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -6,10 +6,16 @@ from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX -from multiversx_sdk_cli.dns import (compute_dns_address_for_shard_id, - dns_address_for_name, name_hash, register, - registration_cost, resolve, validate_name, - version) +from multiversx_sdk_cli.dns import ( + compute_dns_address_for_shard_id, + dns_address_for_name, + name_hash, + register, + registration_cost, + resolve, + validate_name, + version, +) from multiversx_sdk_cli.errors import ArgumentsNotProvidedError @@ -17,7 +23,12 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser(subparsers, "dns", "Operations related to the Domain Name Service") subparsers = parser.add_subparsers() - sub = cli_shared.add_command_subparser(subparsers, "dns", "register", "Send a register transaction to the appropriate DNS contract from given user and with given name") + sub = cli_shared.add_command_subparser( + subparsers, + "dns", + "register", + "Send a register transaction to the appropriate DNS contract from given user and with given name", + ) cli_shared.add_outfile_arg(sub) cli_shared.add_broadcast_args(sub, relay=True) cli_shared.add_wallet_args(args, sub) @@ -32,35 +43,80 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) sub.set_defaults(func=dns_resolve) - sub = cli_shared.add_command_subparser(subparsers, "dns", "validate-name", "Asks one of the DNS contracts to validate a name. Can be useful before registering it.") + sub = cli_shared.add_command_subparser( + subparsers, + "dns", + "validate-name", + "Asks one of the DNS contracts to validate a name. Can be useful before registering it.", + ) _add_name_arg(sub) - sub.add_argument("--shard-id", type=int, default=0, help="shard id of the contract to call (default: %(default)s)") + sub.add_argument( + "--shard-id", + type=int, + default=0, + help="shard id of the contract to call (default: %(default)s)", + ) cli_shared.add_proxy_arg(sub) sub.set_defaults(func=dns_validate_name) - sub = cli_shared.add_command_subparser(subparsers, "dns", "name-hash", "The hash of a name, as computed by a DNS smart contract") + sub = cli_shared.add_command_subparser( + subparsers, + "dns", + "name-hash", + "The hash of a name, as computed by a DNS smart contract", + ) _add_name_arg(sub) sub.set_defaults(func=get_name_hash) - sub = cli_shared.add_command_subparser(subparsers, "dns", "registration-cost", "Gets the registration cost from a DNS smart contract, by default the one with shard id 0.") - sub.add_argument("--shard-id", type=int, default=0, help="shard id of the contract to call (default: %(default)s)") + sub = cli_shared.add_command_subparser( + subparsers, + "dns", + "registration-cost", + "Gets the registration cost from a DNS smart contract, by default the one with shard id 0.", + ) + sub.add_argument( + "--shard-id", + type=int, + default=0, + help="shard id of the contract to call (default: %(default)s)", + ) cli_shared.add_proxy_arg(sub) sub.set_defaults(func=get_registration_cost) sub = cli_shared.add_command_subparser(subparsers, "dns", "version", "Asks the contract for its version") - sub.add_argument("--shard-id", type=int, default=0, help="shard id of the contract to call (default: %(default)s)") - sub.add_argument("--all", action="store_true", default=False, help="prints a list of all DNS contracts and their current versions (default: %(default)s)") + sub.add_argument( + "--shard-id", + type=int, + default=0, + help="shard id of the contract to call (default: %(default)s)", + ) + sub.add_argument( + "--all", + action="store_true", + default=False, + help="prints a list of all DNS contracts and their current versions (default: %(default)s)", + ) cli_shared.add_proxy_arg(sub) sub.set_defaults(func=get_version) sub = cli_shared.add_command_subparser(subparsers, "dns", "dns-addresses", "Lists all 256 DNS contract addresses") sub.set_defaults(func=print_dns_addresses_table) - sub = cli_shared.add_command_subparser(subparsers, "dns", "dns-address-for-name", "DNS contract address (bech32) that corresponds to a name") + sub = cli_shared.add_command_subparser( + subparsers, + "dns", + "dns-address-for-name", + "DNS contract address (bech32) that corresponds to a name", + ) _add_name_arg(sub) sub.set_defaults(func=get_dns_address_for_name) - sub = cli_shared.add_command_subparser(subparsers, "dns", "dns-address-for-name-hex", "DNS contract address (hex) that corresponds to a name") + sub = cli_shared.add_command_subparser( + subparsers, + "dns", + "dns-address-for-name-hex", + "DNS contract address (hex) that corresponds to a name", + ) _add_name_arg(sub) sub.set_defaults(func=get_dns_address_for_name_hex) @@ -122,7 +178,14 @@ def get_version(args: Any): config = get_config_for_network_providers() proxy = ProxyNetworkProvider(url=args.proxy, config=config) if args.all: - t = PrettyTable(['Shard ID', 'Contract address (bech32)', 'Contract address (hex)', 'Version']) + t = PrettyTable( + [ + "Shard ID", + "Contract address (bech32)", + "Contract address (hex)", + "Version", + ] + ) for shard_id in range(0, 256): address = compute_dns_address_for_shard_id(shard_id) v = version(shard_id, proxy) @@ -134,7 +197,7 @@ def get_version(args: Any): def print_dns_addresses_table(args: Any): - t = PrettyTable(['Shard ID', 'Contract address (bech32)', 'Contract address (hex)']) + t = PrettyTable(["Shard ID", "Contract address (bech32)", "Contract address (hex)"]) for shard_id in range(0, 256): address = compute_dns_address_for_shard_id(shard_id) t.add_row([shard_id, address.to_bech32(), address.to_hex()]) diff --git a/multiversx_sdk_cli/cli_faucet.py b/multiversx_sdk_cli/cli_faucet.py index 878dc7ad..19113ace 100644 --- a/multiversx_sdk_cli/cli_faucet.py +++ b/multiversx_sdk_cli/cli_faucet.py @@ -5,8 +5,10 @@ from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.errors import BadUserInput -from multiversx_sdk_cli.native_auth_client import (NativeAuthClient, - NativeAuthClientConfig) +from multiversx_sdk_cli.native_auth_client import ( + NativeAuthClient, + NativeAuthClientConfig, +) logger = logging.getLogger("cli.faucet") @@ -45,11 +47,7 @@ def faucet(args: Any): token_for_siginig = f"{account.address.to_bech32()}{init_token}" signature = account.sign_message(token_for_siginig.encode()) - access_token = client.get_token( - address=account.address.to_bech32(), - token=init_token, - signature=signature - ) + access_token = client.get_token(address=account.address.to_bech32(), token=init_token, signature=signature) logger.info(f"Requesting funds for address: {account.address.to_bech32()}") call_web_wallet_faucet(wallet_url=wallet, access_token=access_token) diff --git a/multiversx_sdk_cli/cli_ledger.py b/multiversx_sdk_cli/cli_ledger.py index b5d1f0c8..25c2ad1e 100644 --- a/multiversx_sdk_cli/cli_ledger.py +++ b/multiversx_sdk_cli/cli_ledger.py @@ -12,10 +12,21 @@ def setup_parser(subparsers: Any) -> Any: subparsers = parser.add_subparsers() sub = cli_shared.add_command_subparser(subparsers, "ledger", "addresses", "Get the addresses within Ledger") - sub.add_argument("--num-addresses", required=False, type=int, default=10, help="The number of addresses to fetch") + sub.add_argument( + "--num-addresses", + required=False, + type=int, + default=10, + help="The number of addresses to fetch", + ) sub.set_defaults(func=print_addresses) - sub = cli_shared.add_command_subparser(subparsers, "ledger", "version", "Get the version of the MultiversX App for Ledger") + sub = cli_shared.add_command_subparser( + subparsers, + "ledger", + "version", + "Get the version of the MultiversX App for Ledger", + ) sub.set_defaults(func=print_version) return subparsers @@ -25,7 +36,7 @@ def print_addresses(args: Any): ledger_app = LedgerApp() for i in range(args.num_addresses): address = ledger_app.get_address(0, i) - print('account index = %d | address index = %d | address: %s' % (0, i, address)) + print("account index = %d | address index = %d | address: %s" % (0, i, address)) ledger_app.close() diff --git a/multiversx_sdk_cli/cli_localnet.py b/multiversx_sdk_cli/cli_localnet.py index ce62507c..72160751 100644 --- a/multiversx_sdk_cli/cli_localnet.py +++ b/multiversx_sdk_cli/cli_localnet.py @@ -5,19 +5,20 @@ from multiversx_sdk_cli import cli_shared, ux from multiversx_sdk_cli.constants import ONE_YEAR_IN_SECONDS from multiversx_sdk_cli.errors import KnownError -from multiversx_sdk_cli.localnet import (step_build_software, step_clean, - step_config, step_new, - step_prerequisites, step_start) +from multiversx_sdk_cli.localnet import ( + step_build_software, + step_clean, + step_config, + step_new, + step_prerequisites, + step_start, +) logger = logging.getLogger("cli.localnet") def setup_parser(args: List[str], subparsers: Any) -> Any: - parser = cli_shared.add_group_subparser( - subparsers, - "localnet", - "Set up, start and control localnets" - ) + parser = cli_shared.add_group_subparser(subparsers, "localnet", "Set up, start and control localnets") subparsers = parser.add_subparsers() # Setup @@ -25,18 +26,13 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: subparsers, "localnet", "setup", - "Set up a localnet (runs 'prerequisites', 'build' and 'config' in one go)" + "Set up a localnet (runs 'prerequisites', 'build' and 'config' in one go)", ) add_argument_configfile(sub) sub.set_defaults(func=localnet_setup) # New - sub = cli_shared.add_command_subparser( - subparsers, - "localnet", - "new", - "Create a new localnet configuration" - ) + sub = cli_shared.add_command_subparser(subparsers, "localnet", "new", "Create a new localnet configuration") add_argument_configfile(sub) sub.set_defaults(func=localnet_new) @@ -45,7 +41,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: subparsers, "localnet", "prerequisites", - "Download and verify the prerequisites for running a localnet" + "Download and verify the prerequisites for running a localnet", ) add_argument_configfile(sub) sub.set_defaults(func=localnet_prerequisites) @@ -55,21 +51,28 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: subparsers, "localnet", "build", - "Build necessary software for running a localnet" + "Build necessary software for running a localnet", ) add_argument_configfile(sub) - sub.add_argument('--software', choices=["node", "seednode", "proxy"], nargs="+", default=["node", "seednode", "proxy"], help="The software to build (default: %(default)s)") + sub.add_argument( + "--software", + choices=["node", "seednode", "proxy"], + nargs="+", + default=["node", "seednode", "proxy"], + help="The software to build (default: %(default)s)", + ) sub.set_defaults(func=localnet_build) # Start - sub = cli_shared.add_command_subparser( - subparsers, - "localnet", - "start", - "Start a localnet" - ) + sub = cli_shared.add_command_subparser(subparsers, "localnet", "start", "Start a localnet") add_argument_configfile(sub) - sub.add_argument("--stop-after-seconds", type=int, required=False, default=ONE_YEAR_IN_SECONDS, help="Stop the localnet after a given number of seconds (default: %(default)s)") + sub.add_argument( + "--stop-after-seconds", + type=int, + required=False, + default=ONE_YEAR_IN_SECONDS, + help="Stop the localnet after a given number of seconds (default: %(default)s)", + ) sub.set_defaults(func=localnet_start) # Config @@ -77,7 +80,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: subparsers, "localnet", "config", - "Configure a localnet (required before starting it the first time or after clean)" + "Configure a localnet (required before starting it the first time or after clean)", ) add_argument_configfile(sub) sub.set_defaults(func=localnet_config) @@ -87,7 +90,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: subparsers, "localnet", "clean", - "Erase the currently configured localnet (must be already stopped)" + "Erase the currently configured localnet (must be already stopped)", ) add_argument_configfile(sub) sub.set_defaults(func=localnet_clean) @@ -95,7 +98,13 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: def add_argument_configfile(parser: Any): help_config_file = "An optional configuration file describing the localnet" - parser.add_argument("--configfile", type=Path, required=False, default=Path("localnet.toml"), help=help_config_file) + parser.add_argument( + "--configfile", + type=Path, + required=False, + default=Path("localnet.toml"), + help=help_config_file, + ) def localnet_new(args: Any): @@ -103,7 +112,9 @@ def localnet_new(args: Any): step_new.new_config(args.configfile) - ux.show_message("New localnet configuration file created (or already existing). Make sure to inspect it. In order to fetch localnet prerequisites, run:\n\n$ mxpy localnet prerequisites") + ux.show_message( + "New localnet configuration file created (or already existing). Make sure to inspect it. In order to fetch localnet prerequisites, run:\n\n$ mxpy localnet prerequisites" + ) def localnet_clean(args: Any): @@ -169,5 +180,7 @@ def guard_configfile(args: Any): raise KnownError(f"Localnet config file does not exist: {configfile}") if old_configfile_in_workdir.exists(): - logger.error(f"""For less ambiguity, the old "testnet.toml" config file should be removed: {old_configfile_in_workdir}""") + logger.error( + f"""For less ambiguity, the old "testnet.toml" config file should be removed: {old_configfile_in_workdir}""" + ) raise KnownError(f"""Found old "testnet.toml" config file in working directory: {old_configfile_in_workdir}""") diff --git a/multiversx_sdk_cli/cli_output.py b/multiversx_sdk_cli/cli_output.py index 9af681e5..ab0c30aa 100644 --- a/multiversx_sdk_cli/cli_output.py +++ b/multiversx_sdk_cli/cli_output.py @@ -38,7 +38,11 @@ def set_contract_address(self, contract_address: IAddress): def set_awaited_transaction(self, awaited_transaction: TransactionOnNetwork, omitted_fields: List[str] = []): return self.set_transaction_on_network(awaited_transaction, omitted_fields) - def set_transaction_on_network(self, transaction_on_network: TransactionOnNetwork, omitted_fields: List[str] = []): + def set_transaction_on_network( + self, + transaction_on_network: TransactionOnNetwork, + omitted_fields: List[str] = [], + ): self.transaction_on_network = transaction_on_network self.transaction_on_network_omitted_fields = omitted_fields return self @@ -75,11 +79,22 @@ def build(self) -> Dict[str, Any]: return output @classmethod - def describe(cls, with_emitted: bool = True, with_contract: bool = False, with_transaction_on_network: bool = False, with_simulation: bool = False) -> str: + def describe( + cls, + with_emitted: bool = True, + with_contract: bool = False, + with_transaction_on_network: bool = False, + with_simulation: bool = False, + ) -> str: output: Dict[str, Any] = OrderedDict() if with_emitted: - output["emittedTransaction"] = {"nonce": 42, "sender": "alice", "receiver": "bob", "...": "..."} + output["emittedTransaction"] = { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "...", + } output["emittedTransactionData"] = "the transaction data, not encoded" output["emittedTransactionHash"] = "the transaction hash" @@ -87,13 +102,15 @@ def describe(cls, with_emitted: bool = True, with_contract: bool = False, with_t output["contractAddress"] = "the address of the contract" if with_transaction_on_network: - output["transactionOnNetwork"] = {"nonce": 42, "sender": "alice", "receiver": "bob", "...": "..."} + output["transactionOnNetwork"] = { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "...", + } if with_simulation: - output["simulation"] = { - "execution": {"...": "..."}, - "cost": {"...": "..."} - } + output["simulation"] = {"execution": {"...": "..."}, "cost": {"...": "..."}} description = json.dumps(output, indent=4) description_wrapped = f""" diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index fe5f46be..b6d0b2ab 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -10,12 +10,12 @@ from multiversx_sdk_cli import config, errors, utils from multiversx_sdk_cli.accounts import Account, LedgerAccount from multiversx_sdk_cli.cli_output import CLIOutputBuilder -from multiversx_sdk_cli.cli_password import (load_guardian_password, - load_password) -from multiversx_sdk_cli.constants import (DEFAULT_TX_VERSION, - TRANSACTION_OPTIONS_TX_GUARDED) +from multiversx_sdk_cli.cli_password import load_guardian_password, load_password +from multiversx_sdk_cli.constants import ( + DEFAULT_TX_VERSION, + TRANSACTION_OPTIONS_TX_GUARDED, +) from multiversx_sdk_cli.errors import ArgumentsNotProvidedError -from multiversx_sdk_cli.interfaces import ITransaction from multiversx_sdk_cli.ledger.ledger_functions import do_get_ledger_address from multiversx_sdk_cli.simulation import Simulator from multiversx_sdk_cli.transactions import send_and_wait_for_result @@ -32,7 +32,7 @@ def add_group_subparser(subparsers: Any, group: str, description: str) -> Any: group, usage=f"mxpy {group} COMMAND [-h] ...", description=description, - formatter_class=argparse.RawDescriptionHelpFormatter + formatter_class=argparse.RawDescriptionHelpFormatter, ) parser._positionals.title = "COMMANDS" parser._optionals.title = "OPTIONS" @@ -58,38 +58,67 @@ def add_command_subparser(subparsers: Any, group: str, command: str, description command, usage=f"mxpy {group} {command} [-h] ...", description=description, - formatter_class=wider_help_formatter + formatter_class=wider_help_formatter, ) def add_tx_args( - args: List[str], - sub: Any, - with_nonce: bool = True, - with_receiver: bool = True, - with_data: bool = True, - with_estimate_gas: bool = False, - with_relayer_wallet_args: bool = True): + args: List[str], + sub: Any, + with_nonce: bool = True, + with_receiver: bool = True, + with_data: bool = True, + with_estimate_gas: bool = False, + with_relayer_wallet_args: bool = True, +): if with_nonce: - sub.add_argument("--nonce", type=int, required=not ("--recall-nonce" in args), help="# the nonce for the transaction") - sub.add_argument("--recall-nonce", action="store_true", default=False, help="⭮ whether to recall the nonce when creating the transaction (default: %(default)s)") + sub.add_argument( + "--nonce", + type=int, + required=not ("--recall-nonce" in args), + help="# the nonce for the transaction", + ) + sub.add_argument( + "--recall-nonce", + action="store_true", + default=False, + help="⭮ whether to recall the nonce when creating the transaction (default: %(default)s)", + ) if with_receiver: sub.add_argument("--receiver", required=True, help="🖄 the address of the receiver") sub.add_argument("--receiver-username", required=False, help="🖄 the username of the receiver") - sub.add_argument("--gas-price", default=config.DEFAULT_GAS_PRICE, help="⛽ the gas price (default: %(default)d)") + sub.add_argument( + "--gas-price", + default=config.DEFAULT_GAS_PRICE, + help="⛽ the gas price (default: %(default)d)", + ) sub.add_argument("--gas-limit", required=not ("--estimate-gas" in args), help="⛽ the gas limit") if with_estimate_gas: - sub.add_argument("--estimate-gas", action="store_true", default=False, help="⛽ whether to estimate the gas limit (default: %(default)d)") + sub.add_argument( + "--estimate-gas", + action="store_true", + default=False, + help="⛽ whether to estimate the gas limit (default: %(default)d)", + ) sub.add_argument("--value", default="0", help="the value to transfer (default: %(default)s)") if with_data: - sub.add_argument("--data", default="", help="the payload, or 'memo' of the transaction (default: %(default)s)") + sub.add_argument( + "--data", + default="", + help="the payload, or 'memo' of the transaction (default: %(default)s)", + ) sub.add_argument("--chain", help="the chain identifier") - sub.add_argument("--version", type=int, default=DEFAULT_TX_VERSION, help="the transaction version (default: %(default)s)") + sub.add_argument( + "--version", + type=int, + default=DEFAULT_TX_VERSION, + help="the transaction version (default: %(default)s)", + ) sub.add_argument("--relayer", help="the bech32 address of the relayer") if with_relayer_wallet_args: @@ -102,40 +131,137 @@ def add_tx_args( def add_guardian_args(sub: Any): sub.add_argument("--guardian", type=str, help="the address of the guradian", default="") - sub.add_argument("--guardian-service-url", type=str, help="the url of the guardian service", default="") - sub.add_argument("--guardian-2fa-code", type=str, help="the 2fa code for the guardian", default="") + sub.add_argument( + "--guardian-service-url", + type=str, + help="the url of the guardian service", + default="", + ) + sub.add_argument( + "--guardian-2fa-code", + type=str, + help="the 2fa code for the guardian", + default="", + ) def add_wallet_args(args: List[str], sub: Any): - sub.add_argument("--pem", required=check_if_sign_method_required(args, "--pem"), help="🔑 the PEM file, if keyfile not provided") - sub.add_argument("--pem-index", type=int, default=0, help="🔑 the index in the PEM file (default: %(default)s)") - sub.add_argument("--keyfile", required=check_if_sign_method_required(args, "--keyfile"), help="🔑 a JSON keyfile, if PEM not provided") - sub.add_argument("--passfile", help="🔑 a file containing keyfile's password, if keyfile provided") - sub.add_argument("--ledger", action="store_true", required=check_if_sign_method_required(args, "--ledger"), default=False, help="🔐 bool flag for signing transaction using ledger") - sub.add_argument("--ledger-account-index", type=int, default=0, help="🔐 the index of the account when using Ledger") - sub.add_argument("--ledger-address-index", type=int, default=0, help="🔐 the index of the address when using Ledger") + sub.add_argument( + "--pem", + required=check_if_sign_method_required(args, "--pem"), + help="🔑 the PEM file, if keyfile not provided", + ) + sub.add_argument( + "--pem-index", + type=int, + default=0, + help="🔑 the index in the PEM file (default: %(default)s)", + ) + sub.add_argument( + "--keyfile", + required=check_if_sign_method_required(args, "--keyfile"), + help="🔑 a JSON keyfile, if PEM not provided", + ) + sub.add_argument( + "--passfile", + help="🔑 a file containing keyfile's password, if keyfile provided", + ) + sub.add_argument( + "--ledger", + action="store_true", + required=check_if_sign_method_required(args, "--ledger"), + default=False, + help="🔐 bool flag for signing transaction using ledger", + ) + sub.add_argument( + "--ledger-account-index", + type=int, + default=0, + help="🔐 the index of the account when using Ledger", + ) + sub.add_argument( + "--ledger-address-index", + type=int, + default=0, + help="🔐 the index of the address when using Ledger", + ) sub.add_argument("--sender-username", required=False, help="🖄 the username of the sender") def add_guardian_wallet_args(args: List[str], sub: Any): - sub.add_argument("--guardian-pem", required=check_if_sign_method_required(args, "--guardian-pem"), help="🔑 the PEM file, if keyfile not provided") - sub.add_argument("--guardian-pem-index", type=int, default=0, help="🔑 the index in the PEM file (default: %(default)s)") - sub.add_argument("--guardian-keyfile", required=check_if_sign_method_required(args, "--guardian-keyfile"), help="🔑 a JSON keyfile, if PEM not provided") - sub.add_argument("--guardian-passfile", help="🔑 a file containing keyfile's password, if keyfile provided") - sub.add_argument("--guardian-ledger", action="store_true", required=check_if_sign_method_required(args, "--guardian-ledger"), default=False, help="🔐 bool flag for signing transaction using ledger") - sub.add_argument("--guardian-ledger-account-index", type=int, default=0, help="🔐 the index of the account when using Ledger") - sub.add_argument("--guardian-ledger-address-index", type=int, default=0, help="🔐 the index of the address when using Ledger") + sub.add_argument( + "--guardian-pem", + required=check_if_sign_method_required(args, "--guardian-pem"), + help="🔑 the PEM file, if keyfile not provided", + ) + sub.add_argument( + "--guardian-pem-index", + type=int, + default=0, + help="🔑 the index in the PEM file (default: %(default)s)", + ) + sub.add_argument( + "--guardian-keyfile", + required=check_if_sign_method_required(args, "--guardian-keyfile"), + help="🔑 a JSON keyfile, if PEM not provided", + ) + sub.add_argument( + "--guardian-passfile", + help="🔑 a file containing keyfile's password, if keyfile provided", + ) + sub.add_argument( + "--guardian-ledger", + action="store_true", + required=check_if_sign_method_required(args, "--guardian-ledger"), + default=False, + help="🔐 bool flag for signing transaction using ledger", + ) + sub.add_argument( + "--guardian-ledger-account-index", + type=int, + default=0, + help="🔐 the index of the account when using Ledger", + ) + sub.add_argument( + "--guardian-ledger-address-index", + type=int, + default=0, + help="🔐 the index of the address when using Ledger", + ) # Required check not properly working, same for guardian. Will be refactored in the future. def add_relayed_v3_wallet_args(args: List[str], sub: Any): sub.add_argument("--relayer-pem", help="🔑 the PEM file, if keyfile not provided") - sub.add_argument("--relayer-pem-index", type=int, default=0, help="🔑 the index in the PEM file (default: %(default)s)") + sub.add_argument( + "--relayer-pem-index", + type=int, + default=0, + help="🔑 the index in the PEM file (default: %(default)s)", + ) sub.add_argument("--relayer-keyfile", help="🔑 a JSON keyfile, if PEM not provided") - sub.add_argument("--relayer-passfile", help="🔑 a file containing keyfile's password, if keyfile provided") - sub.add_argument("--relayer-ledger", action="store_true", default=False, help="🔐 bool flag for signing transaction using ledger") - sub.add_argument("--relayer-ledger-account-index", type=int, default=0, help="🔐 the index of the account when using Ledger") - sub.add_argument("--relayer-ledger-address-index", type=int, default=0, help="🔐 the index of the address when using Ledger") + sub.add_argument( + "--relayer-passfile", + help="🔑 a file containing keyfile's password, if keyfile provided", + ) + sub.add_argument( + "--relayer-ledger", + action="store_true", + default=False, + help="🔐 bool flag for signing transaction using ledger", + ) + sub.add_argument( + "--relayer-ledger-account-index", + type=int, + default=0, + help="🔐 the index of the account when using Ledger", + ) + sub.add_argument( + "--relayer-ledger-address-index", + type=int, + default=0, + help="🔐 the index of the address when using Ledger", + ) def add_proxy_arg(sub: Any): @@ -144,7 +270,12 @@ def add_proxy_arg(sub: Any): def add_outfile_arg(sub: Any, what: str = ""): what = f"({what})" if what else "" - sub.add_argument("--outfile", type=FileType("w"), default=sys.stdout, help=f"where to save the output {what} (default: stdout)") + sub.add_argument( + "--outfile", + type=FileType("w"), + default=sys.stdout, + help=f"where to save the output {what} (default: stdout)", + ) def add_infile_arg(sub: Any, what: str = ""): @@ -153,13 +284,22 @@ def add_infile_arg(sub: Any, what: str = ""): def add_omit_fields_arg(sub: Any): - sub.add_argument("--omit-fields", default="[]", type=str, required=False, help="omit fields in the output payload (default: %(default)s)") + sub.add_argument( + "--omit-fields", + default="[]", + type=str, + required=False, + help="omit fields in the output payload (default: %(default)s)", + ) def add_token_transfers_args(sub: Any): - sub.add_argument("--token-transfers", nargs='+', - help="token transfers for transfer & execute, as [token, amount] " - "E.g. --token-transfers NFT-123456-0a 1 ESDT-987654 100000000") + sub.add_argument( + "--token-transfers", + nargs="+", + help="token transfers for transfer & execute, as [token, amount] " + "E.g. --token-transfers NFT-123456-0a 1 ESDT-987654 100000000", + ) def parse_omit_fields_arg(args: Any) -> List[str]: @@ -175,7 +315,10 @@ def prepare_account(args: Any): password = load_password(args) account = Account(key_file=args.keyfile, password=password) elif args.ledger: - account = LedgerAccount(account_index=args.ledger_account_index, address_index=args.ledger_address_index) + account = LedgerAccount( + account_index=args.ledger_account_index, + address_index=args.ledger_address_index, + ) else: raise errors.NoWalletProvided() @@ -184,7 +327,10 @@ def prepare_account(args: Any): def prepare_relayer_account(args: Any) -> Account: if args.relayer_ledger: - account = LedgerAccount(account_index=args.relayer_ledger_account_index, address_index=args.relayer_ledger_address_index) + account = LedgerAccount( + account_index=args.relayer_ledger_account_index, + address_index=args.relayer_ledger_address_index, + ) if args.relayer_pem: account = Account(pem_file=args.relayer_pem, pem_index=args.relayer_pem_index) elif args.relayer_keyfile: @@ -203,7 +349,10 @@ def prepare_guardian_account(args: Any): password = load_guardian_password(args) account = Account(key_file=args.guardian_keyfile, password=password) elif args.guardian_ledger: - address = do_get_ledger_address(account_index=args.guardian_ledger_account_index, address_index=args.guardian_ledger_address_index) + address = do_get_ledger_address( + account_index=args.guardian_ledger_account_index, + address_index=args.guardian_ledger_address_index, + ) account = Account(Address.new_from_bech32(address)) else: raise errors.NoWalletProvided() @@ -232,7 +381,9 @@ def prepare_chain_id_in_args(args: Any): fetched_chain_id = proxy.get_network_config().chain_id if args.chain != fetched_chain_id: - show_warning(f"The chain ID you have provided does not match the chain ID you got from the proxy. Will use the proxy's value: '{fetched_chain_id}'") + show_warning( + f"The chain ID you have provided does not match the chain ID you got from the proxy. Will use the proxy's value: '{fetched_chain_id}'" + ) args.chain = fetched_chain_id return # if the CLI provided chain ID is correct, we do not patch the arguments @@ -247,17 +398,34 @@ def prepare_chain_id_in_args(args: Any): def add_broadcast_args(sub: Any, simulate: bool = True, relay: bool = False): - sub.add_argument("--send", action="store_true", default=False, help="✓ whether to broadcast the transaction (default: %(default)s)") + sub.add_argument( + "--send", + action="store_true", + default=False, + help="✓ whether to broadcast the transaction (default: %(default)s)", + ) if simulate: - sub.add_argument("--simulate", action="store_true", default=False, help="whether to simulate the transaction (default: %(default)s)") + sub.add_argument( + "--simulate", + action="store_true", + default=False, + help="whether to simulate the transaction (default: %(default)s)", + ) if relay: - sub.add_argument("--relay", action="store_true", default=False, help="whether to relay the transaction (default: %(default)s)") + sub.add_argument( + "--relay", + action="store_true", + default=False, + help="whether to relay the transaction (default: %(default)s)", + ) def check_broadcast_args(args: Any): if hasattr(args, "relay") and args.relay and args.send: - raise errors.BadUsage("Cannot directly send a relayed transaction. Use 'mxpy tx new --relay' first, then 'mxpy tx send --data-file'") + raise errors.BadUsage( + "Cannot directly send a relayed transaction. Use 'mxpy tx new --relay' first, then 'mxpy tx send --data-file'" + ) if args.send and args.simulate: raise errors.BadUsage("Cannot both 'simulate' and 'send' a transaction") @@ -325,7 +493,7 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL if send_only: log_explorer_transaction( chain=output_transaction["emittedTransaction"]["chainID"], - transaction_hash=output_transaction["emittedTransactionHash"] + transaction_hash=output_transaction["emittedTransactionHash"], ) return output_builder diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index e66c6ffb..692e3925 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -9,9 +9,11 @@ from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.errors import IncorrectWalletError, NoWalletProvided -from multiversx_sdk_cli.transactions import (compute_relayed_v1_data, - do_prepare_transaction, - load_transaction_from_file) +from multiversx_sdk_cli.transactions import ( + compute_relayed_v1_data, + do_prepare_transaction, + load_transaction_from_file, +) logger = logging.getLogger("cli.transactions") @@ -20,34 +22,65 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser(subparsers, "tx", "Create and broadcast Transactions") subparsers = parser.add_subparsers() - sub = cli_shared.add_command_subparser(subparsers, "tx", "new", f"Create a new transaction.{CLIOutputBuilder.describe()}") + sub = cli_shared.add_command_subparser( + subparsers, + "tx", + "new", + f"Create a new transaction.{CLIOutputBuilder.describe()}", + ) _add_common_arguments(args, sub) cli_shared.add_token_transfers_args(sub) cli_shared.add_outfile_arg(sub, what="signed transaction, hash") cli_shared.add_broadcast_args(sub, relay=True) cli_shared.add_proxy_arg(sub) cli_shared.add_guardian_wallet_args(args, sub) - sub.add_argument("--wait-result", action="store_true", default=False, - help="signal to wait for the transaction result - only valid if --send is set") - sub.add_argument("--timeout", default=100, help="max num of seconds to wait for result" - " - only valid if --wait-result is set") + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) sub.set_defaults(func=create_transaction) - sub = cli_shared.add_command_subparser(subparsers, "tx", "send", f"Send a previously saved transaction.{CLIOutputBuilder.describe()}") + sub = cli_shared.add_command_subparser( + subparsers, + "tx", + "send", + f"Send a previously saved transaction.{CLIOutputBuilder.describe()}", + ) cli_shared.add_infile_arg(sub, what="a previously saved transaction") cli_shared.add_outfile_arg(sub, what="the hash") cli_shared.add_proxy_arg(sub) sub.set_defaults(func=send_transaction) - sub = cli_shared.add_command_subparser(subparsers, "tx", "get", f"Get a transaction.{CLIOutputBuilder.describe(with_emitted=False, with_transaction_on_network=True)}") + sub = cli_shared.add_command_subparser( + subparsers, + "tx", + "get", + f"Get a transaction.{CLIOutputBuilder.describe(with_emitted=False, with_transaction_on_network=True)}", + ) sub.add_argument("--hash", required=True, help="the hash") sub.add_argument("--sender", required=False, help="the sender address") - sub.add_argument("--with-results", action="store_true", help="will also return the results of transaction") + sub.add_argument( + "--with-results", + action="store_true", + help="will also return the results of transaction", + ) cli_shared.add_proxy_arg(sub) cli_shared.add_omit_fields_arg(sub) sub.set_defaults(func=get_transaction) - sub = cli_shared.add_command_subparser(subparsers, "tx", "sign", f"Sign a previously saved transaction.{CLIOutputBuilder.describe()}") + sub = cli_shared.add_command_subparser( + subparsers, + "tx", + "sign", + f"Sign a previously saved transaction.{CLIOutputBuilder.describe()}", + ) cli_shared.add_wallet_args(args, sub) cli_shared.add_infile_arg(sub, what="a previously saved transaction") cli_shared.add_outfile_arg(sub, what="the signed transaction") @@ -57,7 +90,12 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_guardian_wallet_args(args, sub) sub.set_defaults(func=sign_transaction) - sub = cli_shared.add_command_subparser(subparsers, "tx", "relay", f"Relay a previously saved transaction.{CLIOutputBuilder.describe()}") + sub = cli_shared.add_command_subparser( + subparsers, + "tx", + "relay", + f"Relay a previously saved transaction.{CLIOutputBuilder.describe()}", + ) cli_shared.add_relayed_v3_wallet_args(args, sub) cli_shared.add_infile_arg(sub, what="a previously saved transaction") cli_shared.add_outfile_arg(sub, what="the relayer signed transaction") diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 106f403c..a7e4e016 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -5,18 +5,28 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: - parser = cli_shared.add_group_subparser(subparsers, "validator", "Stake, UnStake, UnBond, Unjail and other " - "actions useful for " - "Validators") + parser = cli_shared.add_group_subparser( + subparsers, + "validator", + "Stake, UnStake, UnBond, Unjail and other " "actions useful for " "Validators", + ) subparsers = parser.add_subparsers() sub = cli_shared.add_command_subparser(subparsers, "validator", "stake", "Stake value into the Network") _add_common_arguments(args, sub) sub.add_argument("--reward-address", default="", help="the reward address") - sub.add_argument("--validators-file", required=not (utils.is_arg_present(args, "--top-up")), - help="a JSON file describing the Nodes") - sub.add_argument("--top-up", action="store_true", default=False, - required=not (utils.is_arg_present(args, "--validators-file")), help="Stake value for top up") + sub.add_argument( + "--validators-file", + required=not (utils.is_arg_present(args, "--top-up")), + help="a JSON file describing the Nodes", + ) + sub.add_argument( + "--top-up", + action="store_true", + default=False, + required=not (utils.is_arg_present(args, "--validators-file")), + help="Stake value for top up", + ) sub.set_defaults(func=do_stake) sub = cli_shared.add_command_subparser(subparsers, "validator", "unstake", "Unstake value") @@ -34,8 +44,9 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: _add_nodes_arg(sub) sub.set_defaults(func=do_unbond) - sub = cli_shared.add_command_subparser(subparsers, "validator", "change-reward-address", - "Change the reward address") + sub = cli_shared.add_command_subparser( + subparsers, "validator", "change-reward-address", "Change the reward address" + ) _add_common_arguments(args, sub) sub.add_argument("--reward-address", required=True, help="the new reward address") sub.set_defaults(func=change_reward_address) @@ -44,17 +55,26 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: _add_common_arguments(args, sub) sub.set_defaults(func=do_claim) - sub = cli_shared.add_command_subparser(subparsers, "validator", "unstake-nodes", "Unstake-nodes will unstake " - "nodes for provided bls keys") + sub = cli_shared.add_command_subparser( + subparsers, + "validator", + "unstake-nodes", + "Unstake-nodes will unstake " "nodes for provided bls keys", + ) _add_common_arguments(args, sub) _add_nodes_arg(sub) sub.set_defaults(func=do_unstake_nodes) - sub = cli_shared.add_command_subparser(subparsers, "validator", "unstake-tokens", "This command will un-stake the " - "given amount (if value is " - "greater than the existing " - "topUp value, it will unStake " - "one or several nodes)") + sub = cli_shared.add_command_subparser( + subparsers, + "validator", + "unstake-tokens", + "This command will un-stake the " + "given amount (if value is " + "greater than the existing " + "topUp value, it will unStake " + "one or several nodes)", + ) _add_common_arguments(args, sub) sub.add_argument("--unstake-value", default=0, help="the unstake value") sub.set_defaults(func=do_unstake_tokens) @@ -64,19 +84,31 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: _add_nodes_arg(sub) sub.set_defaults(func=do_unbond_nodes) - sub = cli_shared.add_command_subparser(subparsers, "validator", "unbond-tokens", "It will unBond tokens, if " - "provided value is bigger that " - "topUp value will unBond nodes") + sub = cli_shared.add_command_subparser( + subparsers, + "validator", + "unbond-tokens", + "It will unBond tokens, if " "provided value is bigger that " "topUp value will unBond nodes", + ) _add_common_arguments(args, sub) sub.add_argument("--unbond-value", default=0, help="the unbond value") sub.set_defaults(func=do_unbond_tokens) - sub = cli_shared.add_command_subparser(subparsers, "validator", "clean-registered-data", "Deletes duplicated keys " - "from registered data") + sub = cli_shared.add_command_subparser( + subparsers, + "validator", + "clean-registered-data", + "Deletes duplicated keys " "from registered data", + ) _add_common_arguments(args, sub) sub.set_defaults(func=do_clean_registered_data) - sub = cli_shared.add_command_subparser(subparsers, "validator", "restake-unstaked-nodes", "It will reStake UnStaked nodes") + sub = cli_shared.add_command_subparser( + subparsers, + "validator", + "restake-unstaked-nodes", + "It will reStake UnStaked nodes", + ) _add_common_arguments(args, sub) _add_nodes_arg(sub) sub.set_defaults(func=do_restake_unstaked_nodes) @@ -95,7 +127,11 @@ def _add_common_arguments(args: List[str], sub: Any): def _add_nodes_arg(sub: Any): - sub.add_argument("--nodes-public-keys", required=True, help="the public keys of the nodes as CSV (addrA,addrB)") + sub.add_argument( + "--nodes-public-keys", + required=True, + help="the public keys of the nodes as CSV (addrA,addrB)", + ) def do_stake(args: Any): diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 1f8b2082..6a9cb92f 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -5,15 +5,13 @@ from pathlib import Path from typing import Any, List, Optional, Tuple -from multiversx_sdk import (Address, Mnemonic, UserPEM, UserSecretKey, - UserWallet) +from multiversx_sdk import Address, Mnemonic, UserPEM, UserSecretKey, UserWallet from multiversx_sdk.core.address import get_shard_of_pubkey from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS -from multiversx_sdk_cli.errors import (BadUserInput, KnownError, - WalletGenerationError) +from multiversx_sdk_cli.errors import BadUserInput, KnownError, WalletGenerationError from multiversx_sdk_cli.sign_verify import SignedMessage, sign_message from multiversx_sdk_cli.ux import show_critical_error, show_message @@ -33,7 +31,11 @@ WALLET_FORMAT_PEM, ] -WALLET_FORMATS_AND_ADDRESSES = [*WALLET_FORMATS, WALLET_FORMAT_ADDRESS_BECH32, WALLET_FORMAT_ADDRESS_HEX] +WALLET_FORMATS_AND_ADDRESSES = [ + *WALLET_FORMATS, + WALLET_FORMAT_ADDRESS_BECH32, + WALLET_FORMAT_ADDRESS_HEX, +] MAX_ITERATIONS_FOR_GENERATING_WALLET = 100 CURRENT_SHARDS = [i for i in range(NUMBER_OF_SHARDS)] @@ -43,7 +45,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser( subparsers, "wallet", - "Create wallet, derive secret key from mnemonic, bech32 address helpers etc." + "Create wallet, derive secret key from mnemonic, bech32 address helpers etc.", ) subparsers = parser.add_subparsers() @@ -51,59 +53,93 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: subparsers, "wallet", "new", - "Create a new wallet and print its mnemonic; optionally save as password-protected JSON (recommended) or PEM (not recommended)" + "Create a new wallet and print its mnemonic; optionally save as password-protected JSON (recommended) or PEM (not recommended)", + ) + sub.add_argument( + "--format", + choices=WALLET_FORMATS, + help="the format of the generated wallet file (default: %(default)s)", + default=None, + ) + sub.add_argument( + "--outfile", + help="the output path and base file name for the generated wallet files (default: %(default)s)", + type=str, + ) + sub.add_argument( + "--address-hrp", + help=f"the human-readable part of the address, when format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM} (default: %(default)s)", + type=str, + default=get_address_hrp(), + ) + sub.add_argument( + "--shard", + type=int, + help="the shard in which the address will be generated; (default: random)", ) - sub.add_argument("--format", choices=WALLET_FORMATS, help="the format of the generated wallet file (default: %(default)s)", default=None) - sub.add_argument("--outfile", help="the output path and base file name for the generated wallet files (default: %(default)s)", type=str) - sub.add_argument("--address-hrp", help=f"the human-readable part of the address, when format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM} (default: %(default)s)", type=str, default=get_address_hrp()) - sub.add_argument("--shard", type=int, help="the shard in which the address will be generated; (default: random)") sub.set_defaults(func=wallet_new) sub = cli_shared.add_command_subparser( - subparsers, - "wallet", - "convert", - "Convert a wallet from one format to another" + subparsers, "wallet", "convert", "Convert a wallet from one format to another" ) sub.add_argument("--infile", help="path to the input file") sub.add_argument("--outfile", help="path to the output file") - sub.add_argument("--in-format", required=True, choices=WALLET_FORMATS, help="the format of the input file") - sub.add_argument("--out-format", required=True, choices=WALLET_FORMATS_AND_ADDRESSES, help="the format of the output file") - sub.add_argument("--address-index", help=f"the address index, if input format is {WALLET_FORMAT_RAW_MNEMONIC}, {WALLET_FORMAT_KEYSTORE_MNEMONIC} or {WALLET_FORMAT_PEM} (with multiple entries) and the output format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM}", type=int, default=0) - sub.add_argument("--address-hrp", help=f"the human-readable part of the address, when the output format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM} (default: %(default)s)", type=str, default=get_address_hrp()) + sub.add_argument( + "--in-format", + required=True, + choices=WALLET_FORMATS, + help="the format of the input file", + ) + sub.add_argument( + "--out-format", + required=True, + choices=WALLET_FORMATS_AND_ADDRESSES, + help="the format of the output file", + ) + sub.add_argument( + "--address-index", + help=f"the address index, if input format is {WALLET_FORMAT_RAW_MNEMONIC}, {WALLET_FORMAT_KEYSTORE_MNEMONIC} or {WALLET_FORMAT_PEM} (with multiple entries) and the output format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM}", + type=int, + default=0, + ) + sub.add_argument( + "--address-hrp", + help=f"the human-readable part of the address, when the output format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM} (default: %(default)s)", + type=str, + default=get_address_hrp(), + ) sub.set_defaults(func=convert_wallet) sub = cli_shared.add_command_subparser( subparsers, "wallet", "bech32", - "Helper for encoding and decoding bech32 addresses" + "Helper for encoding and decoding bech32 addresses", ) sub.add_argument("value", help="the value to encode or decode") group = sub.add_mutually_exclusive_group(required=True) group.add_argument("--encode", action="store_true", help="whether to encode") group.add_argument("--decode", action="store_true", help="whether to decode") - sub.add_argument("--hrp", type=str, help="the human readable part; only used for encoding to bech32 (default: %(default)s)", default=get_address_hrp()) + sub.add_argument( + "--hrp", + type=str, + help="the human readable part; only used for encoding to bech32 (default: %(default)s)", + default=get_address_hrp(), + ) sub.set_defaults(func=do_bech32) - sub = cli_shared.add_command_subparser( - subparsers, - "wallet", - "sign-message", - "Sign a message" - ) + sub = cli_shared.add_command_subparser(subparsers, "wallet", "sign-message", "Sign a message") sub.add_argument("--message", required=True, help="the message you want to sign") cli_shared.add_wallet_args(args, sub) sub.set_defaults(func=sign_user_message) - sub = cli_shared.add_command_subparser( - subparsers, - "wallet", - "verify-message", - "Verify a previously signed message" - ) + sub = cli_shared.add_command_subparser(subparsers, "wallet", "verify-message", "Verify a previously signed message") sub.add_argument("--address", required=True, help="the bech32 address of the signer") - sub.add_argument("--message", required=True, help="the previously signed message(readable text, as it was signed)") + sub.add_argument( + "--message", + required=True, + help="the previously signed message(readable text, as it was signed)", + ) sub.add_argument("--signature", required=True, help="the signature in hex format") sub.set_defaults(func=verify_signed_message) @@ -201,7 +237,9 @@ def convert_wallet(args: Any): print(output_text) -def _load_wallet(input_text: str, in_format: str, address_index: int) -> Tuple[Optional[Mnemonic], Optional[UserSecretKey]]: +def _load_wallet( + input_text: str, in_format: str, address_index: int +) -> Tuple[Optional[Mnemonic], Optional[UserSecretKey]]: if in_format == WALLET_FORMAT_RAW_MNEMONIC: input_text = " ".join(input_text.split()) mnemonic = Mnemonic(input_text) @@ -223,15 +261,17 @@ def _load_wallet(input_text: str, in_format: str, address_index: int) -> Tuple[O secret_key = UserPEM.from_text(input_text, address_index).secret_key return None, secret_key - raise KnownError(f"Cannot load wallet, unknown input format: <{in_format}>. Make sure to use one of following: {WALLET_FORMATS}.") + raise KnownError( + f"Cannot load wallet, unknown input format: <{in_format}>. Make sure to use one of following: {WALLET_FORMATS}." + ) def _create_wallet_content( - out_format: str, - mnemonic: Optional[Mnemonic], - secret_key: Optional[UserSecretKey], - address_index: int, - address_hrp: str + out_format: str, + mnemonic: Optional[Mnemonic], + secret_key: Optional[UserSecretKey], + address_index: int, + address_hrp: str, ) -> str: if out_format == WALLET_FORMAT_RAW_MNEMONIC: if mnemonic is None: @@ -282,7 +322,9 @@ def _create_wallet_content( pubkey = secret_key.generate_public_key() return pubkey.hex() - raise KnownError(f"Cannot create wallet, unknown output format: <{out_format}>. Make sure to use one of following: {WALLET_FORMATS}.") + raise KnownError( + f"Cannot create wallet, unknown output format: <{out_format}>. Make sure to use one of following: {WALLET_FORMATS}." + ) def do_bech32(args: Any): diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 621ab5b1..a9abbe7d 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -110,7 +110,7 @@ def create_new_config(name: str, template: str): new_config = data["configurations"][template] data["active"] = name - data.setdefault('configurations', {}) + data.setdefault("configurations", {}) data["configurations"][name] = new_config write_file(data) @@ -130,13 +130,13 @@ def _guard_valid_name(name: str): def _guard_valid_config_name(config: Any, name: str): - configurations = config.get('configurations', {}) + configurations = config.get("configurations", {}) if name not in configurations: raise errors.UnknownConfigurationError(name) def _guard_config_unique(config: Any, name: str): - configurations = config.get('configurations', {}) + configurations = config.get("configurations", {}) if name in configurations: raise errors.ConfigurationShouldBeUniqueError(name) @@ -166,7 +166,7 @@ def get_defaults() -> Dict[str, Any]: "dependencies.testwallets.urlTemplate.windows": "https://github.com/multiversx/mx-sdk-testwallets/archive/{TAG}.tar.gz", "dependencies.wasm-opt.tag": "0.112.0", "github_api_token": "", - "default_address_hrp": "erd" + "default_address_hrp": "erd", } @@ -216,7 +216,7 @@ def add_config_args(argv: List[str]) -> List[str]: def determine_final_args(argv: List[str], config_args: Dict[str, Any]) -> List[str]: extra_args: List[str] = [] for key, value in config_args.items(): - key_arg = f'--{key}' + key_arg = f"--{key}" # arguments from the command line override the config if key_arg in argv: continue @@ -233,7 +233,7 @@ def determine_final_args(argv: List[str], config_args: Dict[str, Any]) -> List[s # the verbose flag is an exception since it has to go before the command and subcommand # eg. mxpy --verbose contract deploy - verbose_flag = '--verbose' + verbose_flag = "--verbose" pre_args = [] if verbose_flag in extra_args: extra_args.remove(verbose_flag) diff --git a/multiversx_sdk_cli/contract_verification.py b/multiversx_sdk_cli/contract_verification.py index cbad2d3d..1b176668 100644 --- a/multiversx_sdk_cli/contract_verification.py +++ b/multiversx_sdk_cli/contract_verification.py @@ -25,7 +25,7 @@ def __init__( source_code: Dict[str, Any], signature: bytes, docker_image: str, - contract_variant: Optional[str] + contract_variant: Optional[str], ) -> None: self.contract = contract self.source_code = source_code @@ -40,13 +40,19 @@ def to_dictionary(self) -> Dict[str, Any]: "contract": self.contract.bech32(), "dockerImage": self.docker_image, "sourceCode": self.source_code, - "contractVariant": self.contract_variant - } + "contractVariant": self.contract_variant, + }, } class ContractVerificationPayload: - def __init__(self, contract: Address, source_code: Dict[str, Any], docker_image: str, contract_variant: Optional[str]) -> None: + def __init__( + self, + contract: Address, + source_code: Dict[str, Any], + docker_image: str, + contract_variant: Optional[str], + ) -> None: self.contract = contract self.source_code = source_code self.docker_image = docker_image @@ -57,24 +63,27 @@ def serialize(self) -> str: "contract": self.contract.to_bech32(), "dockerImage": self.docker_image, "sourceCode": self.source_code, - "contractVariant": self.contract_variant + "contractVariant": self.contract_variant, } - return json.dumps(payload, separators=(',', ':')) + return json.dumps(payload, separators=(",", ":")) def trigger_contract_verification( - packaged_source: Path, - owner: Account, - contract: Address, - verifier_url: str, - docker_image: str, - contract_variant: Optional[str]): + packaged_source: Path, + owner: Account, + contract: Address, + verifier_url: str, + docker_image: str, + contract_variant: Optional[str], +): source_code = read_json_file(packaged_source) payload = ContractVerificationPayload(contract, source_code, docker_image, contract_variant).serialize() signature = _create_request_signature(owner, contract, payload.encode()) - contract_verification = ContractVerificationRequest(contract, source_code, signature, docker_image, contract_variant) + contract_verification = ContractVerificationRequest( + contract, source_code, signature, docker_image, contract_variant + ) request_dictionary = contract_verification.to_dictionary() diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index e00281a8..42984dee 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -2,11 +2,28 @@ from pathlib import Path from typing import Any, List, Optional, Protocol, Union -from multiversx_sdk import (Address, - SmartContractController, SmartContractQuery, SmartContractQueryResponse, - SmartContractTransactionsFactory, Token, - TokenComputer, TokenTransfer, Transaction, TransactionsFactoryConfig, TransactionOnNetwork, AwaitingOptions) -from multiversx_sdk.abi import Abi, BytesValue, BigUIntValue, AddressValue, BoolValue, StringValue +from multiversx_sdk import ( + Address, + AwaitingOptions, + SmartContractController, + SmartContractQuery, + SmartContractQueryResponse, + SmartContractTransactionsFactory, + Token, + TokenComputer, + TokenTransfer, + Transaction, + TransactionOnNetwork, + TransactionsFactoryConfig, +) +from multiversx_sdk.abi import ( + Abi, + AddressValue, + BigUIntValue, + BoolValue, + BytesValue, + StringValue, +) from multiversx_sdk_cli import errors from multiversx_sdk_cli.accounts import Account @@ -20,6 +37,7 @@ STR_PREFIX = "str:" +# fmt: off class INetworkProvider(Protocol): def query_contract(self, query: SmartContractQuery) -> SmartContractQueryResponse: ... @@ -28,6 +46,7 @@ def await_transaction_completed( self, transaction_hash: Union[bytes, str], options: Optional[AwaitingOptions] = None ) -> TransactionOnNetwork: ... +# fmt: on class SmartContract: @@ -36,21 +55,23 @@ def __init__(self, config: TransactionsFactoryConfig, abi: Optional[Abi] = None) self._config = config self._factory = SmartContractTransactionsFactory(config, abi) - def prepare_deploy_transaction(self, - owner: Account, - bytecode: Path, - arguments: Union[List[Any], None], - should_prepare_args: bool, - upgradeable: bool, - readable: bool, - payable: bool, - payable_by_sc: bool, - gas_limit: int, - value: int, - nonce: int, - version: int, - options: int, - guardian: Address) -> Transaction: + def prepare_deploy_transaction( + self, + owner: Account, + bytecode: Path, + arguments: Union[List[Any], None], + should_prepare_args: bool, + upgradeable: bool, + readable: bool, + payable: bool, + payable_by_sc: bool, + gas_limit: int, + value: int, + nonce: int, + version: int, + options: int, + guardian: Address, + ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: args = self._prepare_args_for_factory(args) @@ -64,7 +85,7 @@ def prepare_deploy_transaction(self, is_upgradeable=upgradeable, is_readable=readable, is_payable=payable, - is_payable_by_sc=payable_by_sc + is_payable_by_sc=payable_by_sc, ) tx.nonce = nonce tx.version = version @@ -74,19 +95,21 @@ def prepare_deploy_transaction(self, return tx - def prepare_execute_transaction(self, - caller: Account, - contract: Address, - function: str, - arguments: Union[List[Any], None], - should_prepare_args: bool, - gas_limit: int, - value: int, - transfers: Union[List[str], None], - nonce: int, - version: int, - options: int, - guardian: Address) -> Transaction: + def prepare_execute_transaction( + self, + caller: Account, + contract: Address, + function: str, + arguments: Union[List[Any], None], + should_prepare_args: bool, + gas_limit: int, + value: int, + transfers: Union[List[str], None], + nonce: int, + version: int, + options: int, + guardian: Address, + ) -> Transaction: token_transfers = self._prepare_token_transfers(transfers) if transfers else [] args = arguments if arguments else [] @@ -100,7 +123,7 @@ def prepare_execute_transaction(self, gas_limit=gas_limit, arguments=args, native_transfer_amount=value, - token_transfers=token_transfers + token_transfers=token_transfers, ) tx.nonce = nonce tx.version = version @@ -110,22 +133,24 @@ def prepare_execute_transaction(self, return tx - def prepare_upgrade_transaction(self, - owner: Account, - contract: Address, - bytecode: Path, - arguments: Union[List[str], None], - should_prepare_args: bool, - upgradeable: bool, - readable: bool, - payable: bool, - payable_by_sc: bool, - gas_limit: int, - value: int, - nonce: int, - version: int, - options: int, - guardian: Address) -> Transaction: + def prepare_upgrade_transaction( + self, + owner: Account, + contract: Address, + bytecode: Path, + arguments: Union[List[str], None], + should_prepare_args: bool, + upgradeable: bool, + readable: bool, + payable: bool, + payable_by_sc: bool, + gas_limit: int, + value: int, + nonce: int, + version: int, + options: int, + guardian: Address, + ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: args = self._prepare_args_for_factory(args) @@ -140,7 +165,7 @@ def prepare_upgrade_transaction(self, is_upgradeable=upgradeable, is_readable=readable, is_payable=payable, - is_payable_by_sc=payable_by_sc + is_payable_by_sc=payable_by_sc, ) tx.nonce = nonce tx.version = version @@ -150,12 +175,14 @@ def prepare_upgrade_transaction(self, return tx - def query_contract(self, - contract_address: Address, - proxy: INetworkProvider, - function: str, - arguments: Optional[List[Any]], - should_prepare_args: bool) -> List[Any]: + def query_contract( + self, + contract_address: Address, + proxy: INetworkProvider, + function: str, + arguments: Optional[List[Any]], + should_prepare_args: bool, + ) -> List[Any]: args = arguments if arguments else [] if should_prepare_args: args = self._prepare_args_for_factory(args) @@ -163,11 +190,7 @@ def query_contract(self, sc_query_controller = SmartContractController(self._config.chain_id, proxy, self._abi) try: - response = sc_query_controller.query( - contract=contract_address, - function=function, - arguments=args - ) + response = sc_query_controller.query(contract=contract_address, function=function, arguments=args) except Exception as e: raise errors.QueryContractError("Couldn't query contract: ", e) @@ -203,14 +226,16 @@ def _prepare_args_for_factory(self, arguments: List[str]) -> List[Any]: elif arg.lower() == TRUE_STR_LOWER: args.append(BoolValue(True)) elif arg.startswith(STR_PREFIX): - args.append(StringValue(arg[len(STR_PREFIX):])) + args.append(StringValue(arg[len(STR_PREFIX) :])) else: - raise errors.BadUserInput(f"Unknown argument type for argument: `{arg}`. Use `mxpy contract --help` to check all supported arguments") + raise errors.BadUserInput( + f"Unknown argument type for argument: `{arg}`. Use `mxpy contract --help` to check all supported arguments" + ) return args def _hex_to_bytes(self, arg: str): - argument = arg[len(HEX_PREFIX):] + argument = arg[len(HEX_PREFIX) :] argument = argument.upper() argument = ensure_even_length(argument) return bytes.fromhex(argument) @@ -252,7 +277,7 @@ def _to_hex(arg: str): def _prepare_hexadecimal(argument: str) -> str: - argument = argument[len(HEX_PREFIX):] + argument = argument[len(HEX_PREFIX) :] argument = argument.upper() argument = ensure_even_length(argument) @@ -271,12 +296,12 @@ def _prepare_decimal(argument: str) -> str: raise errors.UnknownArgumentFormat(argument) as_number = int(argument) - as_hexstring = hex(as_number)[len(HEX_PREFIX):] + as_hexstring = hex(as_number)[len(HEX_PREFIX) :] as_hexstring = ensure_even_length(as_hexstring) return as_hexstring.upper() def ensure_even_length(string: str) -> str: if len(string) % 2 == 1: - return '0' + string + return "0" + string return string diff --git a/multiversx_sdk_cli/cosign_transaction.py b/multiversx_sdk_cli/cosign_transaction.py index 5e019fd8..f269ac4c 100644 --- a/multiversx_sdk_cli/cosign_transaction.py +++ b/multiversx_sdk_cli/cosign_transaction.py @@ -1,7 +1,7 @@ from typing import Any -from multiversx_sdk import Transaction import requests +from multiversx_sdk import Transaction from multiversx_sdk_cli.errors import GuardianServiceError @@ -9,7 +9,7 @@ def cosign_transaction(transaction: Transaction, service_url: str, guardian_code: str) -> Transaction: payload = { "code": f"{guardian_code}", - "transactions": [transaction.to_dictionary()] + "transactions": [transaction.to_dictionary()], } # we call sign-multiple-transactions to be allowed a bigger payload (e.g. deploying large contracts) diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation/staking_provider.py index b06fcff7..82453d63 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -1,15 +1,21 @@ from pathlib import Path from typing import Any, List, Protocol, Tuple -from multiversx_sdk import (Address, DelegationTransactionsFactory, - Transaction, TransactionsFactoryConfig, ValidatorPublicKey) -from multiversx_sdk.abi import Serializer, BigUIntValue +from multiversx_sdk import ( + Address, + DelegationTransactionsFactory, + Transaction, + TransactionsFactoryConfig, + ValidatorPublicKey, +) +from multiversx_sdk.abi import BigUIntValue, Serializer from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.errors import BadUsage from multiversx_sdk_cli.validators.validators_file import ValidatorsFile +# fmt: off class IAccount(Protocol): @property def address(self) -> Address: @@ -19,6 +25,7 @@ def address(self) -> Address: def sign_transaction(self, transaction: Transaction) -> str: ... +# fmt: on class DelegationOperations: @@ -30,7 +37,7 @@ def prepare_transaction_for_new_delegation_contract(self, owner: IAccount, args: sender=owner.address, total_delegation_cap=int(args.total_delegation_cap), service_fee=int(args.service_fee), - amount=int(args.value) + amount=int(args.value), ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -52,7 +59,7 @@ def prepare_transaction_for_adding_nodes(self, owner: IAccount, args: Any) -> Tr sender=owner.address, delegation_contract=delegation_contract, public_keys=public_keys, - signed_messages=signed_messages + signed_messages=signed_messages, ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -74,7 +81,7 @@ def prepare_transaction_for_removing_nodes(self, owner: IAccount, args: Any) -> tx = self._factory.create_transaction_for_removing_nodes( sender=owner.address, delegation_contract=delegation_contract, - public_keys=public_keys + public_keys=public_keys, ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -96,7 +103,7 @@ def prepare_transaction_for_staking_nodes(self, owner: IAccount, args: Any) -> T tx = self._factory.create_transaction_for_staking_nodes( sender=owner.address, delegation_contract=delegation_contract, - public_keys=public_keys + public_keys=public_keys, ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -118,7 +125,7 @@ def prepare_transaction_for_unbonding_nodes(self, owner: IAccount, args: Any) -> tx = self._factory.create_transaction_for_unbonding_nodes( sender=owner.address, delegation_contract=delegation_contract, - public_keys=public_keys + public_keys=public_keys, ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -140,7 +147,7 @@ def prepare_transaction_for_unstaking_nodes(self, owner: IAccount, args: Any) -> tx = self._factory.create_transaction_for_unstaking_nodes( sender=owner.address, delegation_contract=delegation_contract, - public_keys=public_keys + public_keys=public_keys, ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -164,7 +171,7 @@ def prepare_transaction_for_unjailing_nodes(self, owner: IAccount, args: Any) -> sender=owner.address, delegation_contract=delegation_contract, public_keys=public_keys, - amount=amount + amount=amount, ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -185,7 +192,7 @@ def prepare_transaction_for_delegating(self, owner: IAccount, args: Any) -> Tran tx = self._factory.create_transaction_for_delegating( sender=owner.address, delegation_contract=delegation_contract, - amount=int(args.value) + amount=int(args.value), ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -203,8 +210,7 @@ def prepare_transaction_for_claiming_rewards(self, owner: IAccount, args: Any) - delegation_contract = Address.new_from_bech32(args.delegation_contract) tx = self._factory.create_transaction_for_claiming_rewards( - sender=owner.address, - delegation_contract=delegation_contract + sender=owner.address, delegation_contract=delegation_contract ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -222,8 +228,7 @@ def prepare_transaction_for_redelegating_rewards(self, owner: IAccount, args: An delegation_contract = Address.new_from_bech32(args.delegation_contract) tx = self._factory.create_transaction_for_redelegating_rewards( - sender=owner.address, - delegation_contract=delegation_contract + sender=owner.address, delegation_contract=delegation_contract ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -243,7 +248,7 @@ def prepare_transaction_for_undelegating(self, owner: IAccount, args: Any) -> Tr tx = self._factory.create_transaction_for_undelegating( sender=owner.address, delegation_contract=delegation_contract, - amount=int(args.value) + amount=int(args.value), ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -261,8 +266,7 @@ def prepare_transaction_for_withdrawing(self, owner: IAccount, args: Any) -> Tra delegation_contract = Address.new_from_bech32(args.delegation_contract) tx = self._factory.create_transaction_for_withdrawing( - sender=owner.address, - delegation_contract=delegation_contract + sender=owner.address, delegation_contract=delegation_contract ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -282,7 +286,7 @@ def prepare_transaction_for_changing_service_fee(self, owner: IAccount, args: An tx = self._factory.create_transaction_for_changing_service_fee( sender=owner.address, delegation_contract=delegation_contract, - service_fee=int(args.service_fee) + service_fee=int(args.service_fee), ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -302,7 +306,7 @@ def prepare_transaction_for_modifying_delegation_cap(self, owner: IAccount, args tx = self._factory.create_transaction_for_modifying_delegation_cap( sender=owner.address, delegation_contract=delegation_contract, - delegation_cap=int(args.delegation_cap) + delegation_cap=int(args.delegation_cap), ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -321,13 +325,11 @@ def prepare_transaction_for_automatic_activation(self, owner: IAccount, args: An if args.set: tx = self._factory.create_transaction_for_setting_automatic_activation( - sender=owner.address, - delegation_contract=delegation_contract + sender=owner.address, delegation_contract=delegation_contract ) elif args.unset: tx = self._factory.create_transaction_for_unsetting_automatic_activation( - sender=owner.address, - delegation_contract=delegation_contract + sender=owner.address, delegation_contract=delegation_contract ) else: raise BadUsage("Either `--set` or `--unset` should be provided") @@ -349,13 +351,11 @@ def prepare_transaction_for_redelegate_cap(self, owner: IAccount, args: Any) -> if args.set: tx = self._factory.create_transaction_for_setting_cap_check_on_redelegate_rewards( - sender=owner.address, - delegation_contract=delegation_contract + sender=owner.address, delegation_contract=delegation_contract ) elif args.unset: tx = self._factory.create_transaction_for_unsetting_cap_check_on_redelegate_rewards( - sender=owner.address, - delegation_contract=delegation_contract + sender=owner.address, delegation_contract=delegation_contract ) else: raise BadUsage("Either `--set` or `--unset` should be provided") @@ -380,7 +380,7 @@ def prepare_transaction_for_setting_metadata(self, owner: IAccount, args: Any) - delegation_contract=delegation_contract, name=args.name, website=args.website, - identifier=args.identifier + identifier=args.identifier, ) tx.nonce = int(args.nonce) tx.version = int(args.version) @@ -394,8 +394,13 @@ def prepare_transaction_for_setting_metadata(self, owner: IAccount, args: Any) - return tx - def prepare_transaction_for_creating_delegation_contract_from_validator(self, owner: IAccount, args: Any) -> Transaction: - receiver = Address.new_from_hex("000000000000000000010000000000000000000000000000000000000004ffff", get_address_hrp()) + def prepare_transaction_for_creating_delegation_contract_from_validator( + self, owner: IAccount, args: Any + ) -> Transaction: + receiver = Address.new_from_hex( + "000000000000000000010000000000000000000000000000000000000004ffff", + get_address_hrp(), + ) max_cap = int(args.max_cap) fee = int(args.fee) @@ -411,7 +416,7 @@ def prepare_transaction_for_creating_delegation_contract_from_validator(self, ow nonce=int(args.nonce), version=int(args.version), options=int(args.options), - guardian=args.guardian + guardian=args.guardian, ) if args.gas_limit: diff --git a/multiversx_sdk_cli/dependencies/__init__.py b/multiversx_sdk_cli/dependencies/__init__.py index c21f5006..5ecc47b2 100644 --- a/multiversx_sdk_cli/dependencies/__init__.py +++ b/multiversx_sdk_cli/dependencies/__init__.py @@ -1,6 +1,15 @@ -from multiversx_sdk_cli.dependencies.install import (get_all_deps, get_golang, - get_module_by_key, - get_module_directory, - install_module) +from multiversx_sdk_cli.dependencies.install import ( + get_all_deps, + get_golang, + get_module_by_key, + get_module_directory, + install_module, +) -__all__ = ["install_module", "get_module_directory", "get_module_by_key", "get_golang", "get_all_deps"] +__all__ = [ + "install_module", + "get_module_directory", + "get_module_by_key", + "get_golang", + "get_all_deps", +] diff --git a/multiversx_sdk_cli/dependencies/install.py b/multiversx_sdk_cli/dependencies/install.py index 61bf0fe4..e3666ab0 100644 --- a/multiversx_sdk_cli/dependencies/install.py +++ b/multiversx_sdk_cli/dependencies/install.py @@ -3,15 +3,18 @@ from typing import Dict, List from multiversx_sdk_cli import config, errors -from multiversx_sdk_cli.dependencies.modules import (DependencyModule, - GolangModule, Rust, - TestWalletsModule) +from multiversx_sdk_cli.dependencies.modules import ( + DependencyModule, + GolangModule, + Rust, + TestWalletsModule, +) logger = logging.getLogger("install") def install_module(key: str, overwrite: bool = False): - if key == 'all': + if key == "all": modules = get_all_deps() else: modules = [get_module_by_key(key)] @@ -50,11 +53,11 @@ def get_all_deps() -> List[DependencyModule]: return [ Rust(key="rust"), GolangModule(key="golang"), - TestWalletsModule(key="testwallets") + TestWalletsModule(key="testwallets"), ] def get_golang() -> GolangModule: - golang = get_module_by_key('golang') + golang = get_module_by_key("golang") assert isinstance(golang, GolangModule) return golang diff --git a/multiversx_sdk_cli/dependencies/modules.py b/multiversx_sdk_cli/dependencies/modules.py index 3455b24d..4437520e 100644 --- a/multiversx_sdk_cli/dependencies/modules.py +++ b/multiversx_sdk_cli/dependencies/modules.py @@ -6,10 +6,19 @@ from pathlib import Path from typing import Dict, List, Optional -from multiversx_sdk_cli import (config, dependencies, downloader, errors, - myprocess, utils, workstation) +from multiversx_sdk_cli import ( + config, + dependencies, + downloader, + errors, + myprocess, + utils, + workstation, +) from multiversx_sdk_cli.dependencies.resolution import ( - DependencyResolution, get_dependency_resolution) + DependencyResolution, + get_dependency_resolution, +) from multiversx_sdk_cli.ux import show_message, show_warning logger = logging.getLogger("modules") @@ -63,11 +72,13 @@ def get_resolution(self) -> DependencyResolution: class StandaloneModule(DependencyModule): - def __init__(self, - key: str, - aliases: List[str] = [], - repo_name: Optional[str] = None, - organisation: Optional[str] = None): + def __init__( + self, + key: str, + aliases: List[str] = [], + repo_name: Optional[str] = None, + organisation: Optional[str] = None, + ): super().__init__(key, aliases) self.archive_type = "tar.gz" self.repo_name = repo_name @@ -113,8 +124,8 @@ def get_source_directory(self, tag: str) -> Path: tag_no_v = tag_no_v[1:] assert isinstance(self.repo_name, str) - source_folder_option_1 = self.get_directory(tag) / f'{self.repo_name}-{tag_no_v}' - source_folder_option_2 = self.get_directory(tag) / f'{self.repo_name}-{tag}' + source_folder_option_1 = self.get_directory(tag) / f"{self.repo_name}-{tag_no_v}" + source_folder_option_2 = self.get_directory(tag) / f"{self.repo_name}-{tag}" return source_folder_option_1 if source_folder_option_1.exists() else source_folder_option_2 def get_parent_directory(self) -> Path: @@ -165,7 +176,7 @@ def get_env(self) -> Dict[str, str]: "PATH": os.environ.get("PATH", ""), "GOPATH": os.environ.get("GOPATH", ""), "GOCACHE": os.environ.get("GOCACHE", ""), - "GOROOT": os.environ.get("GOROOT", "") + "GOROOT": os.environ.get("GOROOT", ""), } if resolution == DependencyResolution.SDK: current_path = os.environ.get("PATH", "") @@ -178,7 +189,7 @@ def get_env(self) -> Dict[str, str]: "PATH": f"{(directory / 'go' / 'bin')}:{current_path_without_go}", "GOPATH": str(self.get_gopath()), "GOCACHE": str(parent_directory / "GOCACHE"), - "GOROOT": str(directory / "go") + "GOROOT": str(directory / "go"), } raise errors.BadDependencyResolution(self.key, resolution) @@ -202,7 +213,13 @@ def is_installed(self, tag: str) -> bool: logger.info(f"which wasm-opt: {which_wasm_opt}") logger.info(f"which twiggy: {which_twiggy}") - dependencies = [which_rustc, which_cargo, which_sc_meta, which_wasm_opt, which_twiggy] + dependencies = [ + which_rustc, + which_cargo, + which_sc_meta, + which_wasm_opt, + which_twiggy, + ] installed = all(dependency is not None for dependency in dependencies) if installed: @@ -213,7 +230,9 @@ def is_installed(self, tag: str) -> bool: elif "not found" in actual_version_installed: show_warning("You have installed Rust without using `rustup`.") else: - show_warning(f"The Rust version you have installed does not match the recommended version.\nInstalled [{actual_version_installed}], expected [{tag}].") + show_warning( + f"The Rust version you have installed does not match the recommended version.\nInstalled [{actual_version_installed}], expected [{tag}]." + ) return installed @@ -229,7 +248,9 @@ def install(self, overwrite: bool) -> None: tag: str = config.get_dependency_tag(module.key) if not overwrite: - show_warning(f"We recommend using rust {tag}. If you'd like to overwrite your current version please run `mxpy deps install rust --overwrite`.") + show_warning( + f"We recommend using rust {tag}. If you'd like to overwrite your current version please run `mxpy deps install rust --overwrite`." + ) logger.info(f"install: key={self.key}, tag={tag}, overwrite={overwrite}") if overwrite: @@ -241,7 +262,9 @@ def install(self, overwrite: bool) -> None: self._install_sc_meta() self._install_wasm_opt() self._install_twiggy() - show_message("To ensure sc-meta functions correctly, please install all the required dependencies by executing the following command: `sc-meta install all`.") + show_message( + "To ensure sc-meta functions correctly, please install all the required dependencies by executing the following command: `sc-meta install all`." + ) def _check_install_env(self, apply_correction: bool = True): """ @@ -251,16 +274,20 @@ def _check_install_env(self, apply_correction: bool = True): current_cargo_home = os.environ.get("CARGO_HOME", None) current_rustup_home = os.environ.get("RUSTUP_HOME", None) if current_cargo_home: - show_warning(f"""CARGO_HOME variable is set to: {current_cargo_home}. -This may cause problems with the installation.""") + show_warning( + f"""CARGO_HOME variable is set to: {current_cargo_home}. +This may cause problems with the installation.""" + ) if apply_correction: show_warning("CARGO_HOME will be temporarily unset.") os.environ["CARGO_HOME"] = "" if current_rustup_home: - show_warning(f"""RUSTUP_HOME variable is set to: {current_rustup_home}. -This may cause problems with the installation of rust.""") + show_warning( + f"""RUSTUP_HOME variable is set to: {current_rustup_home}. +This may cause problems with the installation of rust.""" + ) if apply_correction: show_warning("RUSTUP_HOME will be temporarily unset.") @@ -278,8 +305,17 @@ def _install_rust(self, tag: str) -> None: else: toolchain = "stable" - args = [str(installer_path), "--verbose", "--default-toolchain", toolchain, "--profile", - "minimal", "--target", "wasm32-unknown-unknown", "-y"] + args = [ + str(installer_path), + "--verbose", + "--default-toolchain", + toolchain, + "--profile", + "minimal", + "--target", + "wasm32-unknown-unknown", + "-y", + ] logger.info("Installing rust.") myprocess.run_process(args) diff --git a/multiversx_sdk_cli/dependency_checker.py b/multiversx_sdk_cli/dependency_checker.py index ffe0d180..abf392a4 100644 --- a/multiversx_sdk_cli/dependency_checker.py +++ b/multiversx_sdk_cli/dependency_checker.py @@ -7,5 +7,7 @@ def check_if_rust_is_installed(): rust_module = Rust(RUST_MODULE_KEY) if not rust_module.is_installed(""): tag = config.get_dependency_tag(RUST_MODULE_KEY) - ux.show_critical_error("Rust is not installed on your machine. Run `mxpy deps install rust --overwrite` and try again.") + ux.show_critical_error( + "Rust is not installed on your machine. Run `mxpy deps install rust --overwrite` and try again." + ) raise errors.DependencyMissing(RUST_MODULE_KEY, tag) diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 55cf047b..6f1df414 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -1,22 +1,31 @@ from typing import Any, List, Optional, Protocol, Union from Cryptodome.Hash import keccak -from multiversx_sdk import Address, AddressComputer, TransactionsFactoryConfig, AwaitingOptions, TransactionOnNetwork -from multiversx_sdk import NetworkConfig +from multiversx_sdk import ( + Address, + AddressComputer, + AwaitingOptions, + NetworkConfig, + TransactionOnNetwork, + TransactionsFactoryConfig, +) from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX from multiversx_sdk_cli.contracts import SmartContract -from multiversx_sdk_cli.transactions import (compute_relayed_v1_data, - do_prepare_transaction) +from multiversx_sdk_cli.transactions import ( + compute_relayed_v1_data, + do_prepare_transaction, +) MaxNumShards = 256 ShardIdentiferLen = 2 InitialDNSAddress = bytes([1] * 32) +# fmt: off class INetworkProvider(Protocol): def query_contract(self, query: Any) -> Any: ... @@ -28,18 +37,14 @@ def await_transaction_completed( self, transaction_hash: Union[bytes, str], options: Optional[AwaitingOptions] = None ) -> TransactionOnNetwork: ... +# fmt: on def resolve(name: str, proxy: INetworkProvider) -> Address: name_arg = "0x{}".format(str.encode(name).hex()) dns_address = dns_address_for_name(name) - response = _query_contract( - contract_address=dns_address, - proxy=proxy, - function="resolve", - args=[name_arg] - ) + response = _query_contract(contract_address=dns_address, proxy=proxy, function="resolve", args=[name_arg]) if len(response) == 0: return Address.new_from_hex(ADDRESS_ZERO_HEX, get_address_hrp()) @@ -56,7 +61,7 @@ def validate_name(name: str, shard_id: int, proxy: INetworkProvider): contract_address=dns_address, proxy=proxy, function="validateName", - args=[name_arg] + args=[name_arg], ) response = response[0] @@ -107,7 +112,7 @@ def registration_cost(shard_id: int, proxy: INetworkProvider) -> int: contract_address=dns_address, proxy=proxy, function="getRegistrationCost", - args=[] + args=[], ) response = response[0] @@ -122,12 +127,7 @@ def registration_cost(shard_id: int, proxy: INetworkProvider) -> int: def version(shard_id: int, proxy: INetworkProvider) -> str: dns_address = compute_dns_address_for_shard_id(shard_id) - response = _query_contract( - contract_address=dns_address, - proxy=proxy, - function="version", - args=[] - ) + response = _query_contract(contract_address=dns_address, proxy=proxy, function="version", args=[]) response = response[0] return bytearray.fromhex(response["returnDataParts"][0]).decode() @@ -140,7 +140,7 @@ def dns_address_for_name(name: str) -> Address: def compute_dns_address_for_shard_id(shard_id: int) -> Address: - deployer_pubkey_prefix = InitialDNSAddress[:len(InitialDNSAddress) - ShardIdentiferLen] + deployer_pubkey_prefix = InitialDNSAddress[: len(InitialDNSAddress) - ShardIdentiferLen] deployer_pubkey = deployer_pubkey_prefix + bytes([0, shard_id]) deployer = Account(address=Address(deployer_pubkey, get_address_hrp())) @@ -165,5 +165,5 @@ def _query_contract(contract_address: Address, proxy: INetworkProvider, function proxy=proxy, function=function, arguments=args, - should_prepare_args=False + should_prepare_args=False, ) diff --git a/multiversx_sdk_cli/docker.py b/multiversx_sdk_cli/docker.py index 02deb8bb..2c087a77 100644 --- a/multiversx_sdk_cli/docker.py +++ b/multiversx_sdk_cli/docker.py @@ -23,14 +23,14 @@ def is_docker_installed(): def run_docker( - image: str, - project_path: Path, - contract: str, - output_path: Path, - no_wasm_opt: bool, - docker_interactive: bool, - docker_tty: bool, - no_default_platform: bool + image: str, + project_path: Path, + contract: str, + output_path: Path, + no_wasm_opt: bool, + docker_interactive: bool, + docker_tty: bool, + no_default_platform: bool, ): docker_mount_args: List[str] = ["--volume", f"{output_path}:/output"] diff --git a/multiversx_sdk_cli/downloader.py b/multiversx_sdk_cli/downloader.py index 93dab7c1..7b49d0db 100644 --- a/multiversx_sdk_cli/downloader.py +++ b/multiversx_sdk_cli/downloader.py @@ -1,4 +1,3 @@ - import logging import sys @@ -9,9 +8,9 @@ logger = logging.getLogger("downloader") CHUNK_SIZE = 1024 * 16 -LINECLEAR = '\r' + ' ' * 20 + '\r' +LINECLEAR = "\r" + " " * 20 + "\r" -PROGRESS_RULER = 'Downloading...\n|_,_,_,_,_,,_,_,_,_,_|' +PROGRESS_RULER = "Downloading...\n|_,_,_,_,_,,_,_,_,_,_|" def download(url: str, filename: str) -> None: @@ -25,22 +24,21 @@ def download(url: str, filename: str) -> None: response = requests.get(url, stream=True) response.raise_for_status() print(PROGRESS_RULER, file=sys.stderr) - print(' ', end='', file=sys.stderr) + print(" ", end="", file=sys.stderr) sys.stderr.flush() total_size = int(response.headers.get("content-length", 0)) chunk_number = 0 progress = 0 - with open(filename, 'wb') as file: + with open(filename, "wb") as file: for chunk in response.iter_content(chunk_size=CHUNK_SIZE): file.write(chunk) progress = _report_download_progress(progress, chunk_number, total_size) chunk_number += 1 file.flush() - print('', file=sys.stderr) + print("", file=sys.stderr) sys.stderr.flush() except requests.HTTPError as err: - raise errors.DownloadError( - f"Could not download [{url}] to [{filename}]") from err + raise errors.DownloadError(f"Could not download [{url}] to [{filename}]") from err logger.info("Download done.") @@ -50,8 +48,8 @@ def _report_download_progress(progress: int, chunk_number: int, total_size: int) num_chunks = int(total_size / CHUNK_SIZE) new_progress = int((chunk_number / num_chunks) * 20) if new_progress > progress: - progress_markers = '·' * (new_progress - progress) - print(progress_markers, end='', file=sys.stderr) + progress_markers = "·" * (new_progress - progress) + print(progress_markers, end="", file=sys.stderr) sys.stderr.flush() return new_progress except ZeroDivisionError: diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 2164ad6d..2ef320b6 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -94,9 +94,11 @@ def __init__(self, input: str, message: str): class ExternalProcessError(KnownError): def __init__(self, command_line: str, message: str): - super().__init__(f"""External process error: + super().__init__( + f"""External process error: Command line: {command_line} -Output: {message}""") +Output: {message}""" + ) class UnknownConfigurationError(KnownError): @@ -161,11 +163,7 @@ def __init__(self, message: str): class ProxyError(KnownError): def __init__(self, message: str, url: str, data: str, code: str): - inner = { - "url": url, - "data": data, - "code": code - } + inner = {"url": url, "data": data, "code": code} super().__init__(message, inner) diff --git a/multiversx_sdk_cli/interfaces.py b/multiversx_sdk_cli/interfaces.py index 69f7d2bb..0c98b6f2 100644 --- a/multiversx_sdk_cli/interfaces.py +++ b/multiversx_sdk_cli/interfaces.py @@ -3,6 +3,7 @@ from multiversx_sdk import Transaction +# fmt: off class IAddress(Protocol): def to_hex(self) -> str: ... diff --git a/multiversx_sdk_cli/ledger/ledger_app_handler.py b/multiversx_sdk_cli/ledger/ledger_app_handler.py index 2ec9e955..18820813 100644 --- a/multiversx_sdk_cli/ledger/ledger_app_handler.py +++ b/multiversx_sdk_cli/ledger/ledger_app_handler.py @@ -3,14 +3,16 @@ from ledgercomm import Transport from multiversx_sdk_cli.errors import LedgerError -from multiversx_sdk_cli.ledger.config import (LedgerAppConfiguration, - load_ledger_config_from_response) +from multiversx_sdk_cli.ledger.config import ( + LedgerAppConfiguration, + load_ledger_config_from_response, +) SIGN_USING_HASH_VERSION = "1.0.11" CONNECTION_ERROR_MSG = "check if device is plugged in, unlocked and on MultiversX app" # Also see: https://github.com/multiversx/mx-sdk-js-hw-provider/blob/main/src/ledgerApp.ts -CLA = 0xed +CLA = 0xED SIGN_RAW_TX_INS = 0x04 SIGN_HASH_TX_INS = 0x07 SIGN_MESSAGE_INS = 0x06 @@ -37,22 +39,22 @@ def close(self): self.transport.close() def set_address(self, account_index: int = 0, address_index: int = 0): - data = account_index.to_bytes(4, byteorder='big') + address_index.to_bytes(4, byteorder='big') - self.transport.send(cla=0xed, ins=0x05, p1=0x00, p2=0x00, cdata=data) + data = account_index.to_bytes(4, byteorder="big") + address_index.to_bytes(4, byteorder="big") + self.transport.send(cla=0xED, ins=0x05, p1=0x00, p2=0x00, cdata=data) sw, _ = self.transport.recv() err = get_error(sw) - if err != '': + if err != "": raise LedgerError(err) def get_address(self, account_index: int = 0, address_index: int = 0) -> str: - data = account_index.to_bytes(4, byteorder='big') + address_index.to_bytes(4, byteorder='big') + data = account_index.to_bytes(4, byteorder="big") + address_index.to_bytes(4, byteorder="big") - self.transport.send(cla=0xed, ins=0x03, p1=0x00, p2=0x00, cdata=data) + self.transport.send(cla=0xED, ins=0x03, p1=0x00, p2=0x00, cdata=data) sw, response = self.transport.recv() assert isinstance(response, bytes) err = get_error(sw) - if err != '': + if err != "": raise LedgerError(CONNECTION_ERROR_MSG + " (" + err + ")") response_body = response[1:] @@ -60,10 +62,10 @@ def get_address(self, account_index: int = 0, address_index: int = 0) -> str: return address def get_app_configuration(self) -> LedgerAppConfiguration: - self.transport.send(cla=0xed, ins=0x02, p1=0x00, p2=0x00, cdata=b"") + self.transport.send(cla=0xED, ins=0x02, p1=0x00, p2=0x00, cdata=b"") sw, response = self.transport.recv() err = get_error(sw) - if err != '': + if err != "": raise LedgerError(CONNECTION_ERROR_MSG + " (" + err + ")") return load_ledger_config_from_response(response) @@ -106,7 +108,7 @@ def _do_sign(self, data: bytes, ins_signing_method: int) -> str: apdu.ins = ins_signing_method apdu.p2 = 0x00 apdu.cla = CLA - apdu.data = data[offset:offset + chunk_size] + apdu.data = data[offset : offset + chunk_size] apdus.append(apdu) @@ -118,20 +120,15 @@ def get_signature_from_apdus(self, apdus: List[Apdu]) -> str: sw: int response: bytes for apdu in apdus: - self.transport.send( - cla=apdu.cla, - ins=apdu.ins, - p1=apdu.p1, - p2=apdu.p2, - cdata=apdu.data) + self.transport.send(cla=apdu.cla, ins=apdu.ins, p1=apdu.p1, p2=apdu.p2, cdata=apdu.data) sw, response = self.transport.recv() assert isinstance(response, bytes) - if len(response) != 65 or response[0] != 64 or get_error(sw) != '': + if len(response) != 65 or response[0] != 64 or get_error(sw) != "": err_message = "signature failed" err = get_error(sw) - if err != '': - err_message += ': ' + err + if err != "": + err_message += ": " + err raise LedgerError(err_message) response_body = response[1:] @@ -141,27 +138,27 @@ def get_signature_from_apdus(self, apdus: List[Apdu]) -> str: def get_error(code: int): switcher = { - 0x9000: '', - 0x6985: 'user denied', - 0x6D00: 'unknown instruction', - 0x6E00: 'wrong cla', - 0x6E10: 'signature failed', - 0x6E01: 'invalid arguments', - 0x6E02: 'invalid message', - 0x6E03: 'invalid p1', - 0x6E04: 'message too long', - 0x6E05: 'receiver too long', - 0x6E06: 'amount too long', - 0x6E07: 'contract data disabled', - 0x6E08: 'message incomplete', - 0x6E09: 'wrong tx version', - 0x6E0A: 'nonce too long', - 0x6E0B: 'invalid amount', - 0x6E0C: 'invalid fee', - 0x6E0D: 'pretty failed', - 0x6E0E: 'data too long', - 0x6E0F: 'wrong tx options', - 0x6E11: 'regular signing is deprecated', + 0x9000: "", + 0x6985: "user denied", + 0x6D00: "unknown instruction", + 0x6E00: "wrong cla", + 0x6E10: "signature failed", + 0x6E01: "invalid arguments", + 0x6E02: "invalid message", + 0x6E03: "invalid p1", + 0x6E04: "message too long", + 0x6E05: "receiver too long", + 0x6E06: "amount too long", + 0x6E07: "contract data disabled", + 0x6E08: "message incomplete", + 0x6E09: "wrong tx version", + 0x6E0A: "nonce too long", + 0x6E0B: "invalid amount", + 0x6E0C: "invalid fee", + 0x6E0D: "pretty failed", + 0x6E0E: "data too long", + 0x6E0F: "wrong tx options", + 0x6E11: "regular signing is deprecated", } return switcher.get(code, "unknown error code: " + hex(code)) diff --git a/multiversx_sdk_cli/ledger/ledger_functions.py b/multiversx_sdk_cli/ledger/ledger_functions.py index 233b8eaa..d1ec64f8 100644 --- a/multiversx_sdk_cli/ledger/ledger_functions.py +++ b/multiversx_sdk_cli/ledger/ledger_functions.py @@ -9,10 +9,7 @@ def do_sign_transaction_with_ledger( - tx_payload: bytes, - account_index: int, - address_index: int, - sign_using_hash: bool + tx_payload: bytes, account_index: int, address_index: int, sign_using_hash: bool ) -> str: ledger_handler = LedgerApp() ledger_handler.set_address(account_index=account_index, address_index=address_index) @@ -24,11 +21,7 @@ def do_sign_transaction_with_ledger( return signature -def do_sign_message_with_ledger( - message_payload: bytes, - account_index: int, - address_index: int -) -> str: +def do_sign_message_with_ledger(message_payload: bytes, account_index: int, address_index: int) -> str: ledger_handler = LedgerApp() ledger_handler.set_address(account_index=account_index, address_index=address_index) diff --git a/multiversx_sdk_cli/localnet/config_default.py b/multiversx_sdk_cli/localnet/config_default.py index 752362aa..49a1ca42 100644 --- a/multiversx_sdk_cli/localnet/config_default.py +++ b/multiversx_sdk_cli/localnet/config_default.py @@ -2,18 +2,19 @@ from multiversx_sdk_cli.localnet.config_general import General from multiversx_sdk_cli.localnet.config_networking import Networking -from multiversx_sdk_cli.localnet.config_sharding import (Metashard, - RegularShards) -from multiversx_sdk_cli.localnet.config_software import (Software, - SoftwareChainGo, - SoftwareChainProxyGo, - SoftwareResolution) +from multiversx_sdk_cli.localnet.config_sharding import Metashard, RegularShards +from multiversx_sdk_cli.localnet.config_software import ( + Software, + SoftwareChainGo, + SoftwareChainProxyGo, + SoftwareResolution, +) general = General( log_level="*:DEBUG", genesis_delay_seconds=10, rounds_per_epoch=100, - round_duration_milliseconds=6000 + round_duration_milliseconds=6000, ) software = Software( @@ -22,15 +23,21 @@ archive_url="https://github.com/multiversx/mx-chain-go/archive/refs/heads/master.zip", archive_download_folder=Path("~/multiversx-sdk") / "localnet_software_remote" / "downloaded" / "mx-chain-go", archive_extraction_folder=Path("~/multiversx-sdk") / "localnet_software_remote" / "extracted" / "mx-chain-go", - local_path=Path("~/multiversx-sdk") / "localnet_software_local" / "mx-chain-go" + local_path=Path("~/multiversx-sdk") / "localnet_software_local" / "mx-chain-go", ), mx_chain_proxy_go=SoftwareChainProxyGo( resolution=SoftwareResolution.Remote, archive_url="https://github.com/multiversx/mx-chain-proxy-go/archive/refs/heads/master.zip", - archive_download_folder=Path("~/multiversx-sdk") / "localnet_software_remote" / "downloaded" / "mx-chain-proxy-go", - archive_extraction_folder=Path("~/multiversx-sdk") / "localnet_software_remote" / "extracted" / "mx-chain-proxy-go", - local_path=Path("~/multiversx-sdk") / "localnet_software_local" / "mx-chain-proxy-go" - ) + archive_download_folder=Path("~/multiversx-sdk") + / "localnet_software_remote" + / "downloaded" + / "mx-chain-proxy-go", + archive_extraction_folder=Path("~/multiversx-sdk") + / "localnet_software_remote" + / "extracted" + / "mx-chain-proxy-go", + local_path=Path("~/multiversx-sdk") / "localnet_software_local" / "mx-chain-proxy-go", + ), ) metashard = Metashard( @@ -55,5 +62,5 @@ port_first_observer=21100, port_first_observer_rest_api=10100, port_first_validator=21500, - port_first_validator_rest_api=10200 + port_first_validator_rest_api=10200, ) diff --git a/multiversx_sdk_cli/localnet/config_general.py b/multiversx_sdk_cli/localnet/config_general.py index a4579924..03b1c943 100644 --- a/multiversx_sdk_cli/localnet/config_general.py +++ b/multiversx_sdk_cli/localnet/config_general.py @@ -4,11 +4,13 @@ class General(ConfigPart): - def __init__(self, - log_level: str, - genesis_delay_seconds: int, - rounds_per_epoch: int, - round_duration_milliseconds: int): + def __init__( + self, + log_level: str, + genesis_delay_seconds: int, + rounds_per_epoch: int, + round_duration_milliseconds: int, + ): self.log_level: str = log_level self.genesis_delay_seconds: int = genesis_delay_seconds self.rounds_per_epoch: int = rounds_per_epoch diff --git a/multiversx_sdk_cli/localnet/config_networking.py b/multiversx_sdk_cli/localnet/config_networking.py index f13f877a..171b29cb 100644 --- a/multiversx_sdk_cli/localnet/config_networking.py +++ b/multiversx_sdk_cli/localnet/config_networking.py @@ -4,16 +4,18 @@ class Networking(ConfigPart): - def __init__(self, - host: str, - port_seednode: int, - port_seednode_rest_api: int, - p2p_id_seednode: str, - port_proxy: int, - port_first_observer: int, - port_first_observer_rest_api: int, - port_first_validator: int, - port_first_validator_rest_api: int): + def __init__( + self, + host: str, + port_seednode: int, + port_seednode_rest_api: int, + p2p_id_seednode: str, + port_proxy: int, + port_first_observer: int, + port_first_observer_rest_api: int, + port_first_validator: int, + port_first_validator_rest_api: int, + ): self.host: str = host self.port_seednode: int = port_seednode self.port_seednode_rest_api: int = port_seednode_rest_api @@ -36,7 +38,9 @@ def _do_override(self, other: Dict[str, Any]): self.port_first_observer = other.get("port_first_observer", self.port_first_observer) self.port_first_observer_rest_api = other.get("port_first_observer_rest_api", self.port_first_observer_rest_api) self.port_first_validator = other.get("port_first_validator", self.port_first_validator) - self.port_first_validator_rest_api = other.get("port_first_validator_rest_api", self.port_first_validator_rest_api) + self.port_first_validator_rest_api = other.get( + "port_first_validator_rest_api", self.port_first_validator_rest_api + ) def get_proxy_url(self) -> str: return f"http://{self.host}:{self.port_proxy}" diff --git a/multiversx_sdk_cli/localnet/config_part.py b/multiversx_sdk_cli/localnet/config_part.py index cccdd17f..9035cfae 100644 --- a/multiversx_sdk_cli/localnet/config_part.py +++ b/multiversx_sdk_cli/localnet/config_part.py @@ -23,10 +23,12 @@ def _validate_overriding_entries(self, overriding: Dict[str, Any]) -> None: unknown_entries = overriding_entries - allowed_entries if unknown_entries: - logger.error(f"""\ + logger.error( + f"""\ Unknown localnet configuration entries: {unknown_entries}. Please check the configuration of the localnet. -For "{self.get_name()}", the allowed entries are: {allowed_entries}.""") +For "{self.get_name()}", the allowed entries are: {allowed_entries}.""" + ) raise UnknownConfigurationError(f"Unknown localnet configuration entries: {unknown_entries}") def _do_override(self, other: Dict[str, Any]) -> None: diff --git a/multiversx_sdk_cli/localnet/config_root.py b/multiversx_sdk_cli/localnet/config_root.py index 575a44ad..af0a42b0 100644 --- a/multiversx_sdk_cli/localnet/config_root.py +++ b/multiversx_sdk_cli/localnet/config_root.py @@ -146,18 +146,22 @@ def api_addresses_sharded_for_proxy_config(self) -> List[Dict[str, Any]]: nodes: List[Dict[str, Any]] = [] for node in self.observers(): - nodes.append({ - "ShardId": int(node.shard), - "Address": node.api_address(), - "Type": "Observer" - }) + nodes.append( + { + "ShardId": int(node.shard), + "Address": node.api_address(), + "Type": "Observer", + } + ) for node in self.validators(): - nodes.append({ - "ShardId": int(node.shard), - "Address": node.api_address(), - "Type": "Validator" - }) + nodes.append( + { + "ShardId": int(node.shard), + "Address": node.api_address(), + "Type": "Validator", + } + ) return nodes diff --git a/multiversx_sdk_cli/localnet/config_sharding.py b/multiversx_sdk_cli/localnet/config_sharding.py index d509a1db..a2e802ba 100644 --- a/multiversx_sdk_cli/localnet/config_sharding.py +++ b/multiversx_sdk_cli/localnet/config_sharding.py @@ -4,10 +4,7 @@ class Metashard(ConfigPart): - def __init__(self, - consensus_size: int, - num_observers: int, - num_validators: int): + def __init__(self, consensus_size: int, num_observers: int, num_validators: int): self.consensus_size: int = consensus_size self.num_observers: int = num_observers self.num_validators: int = num_validators @@ -22,11 +19,13 @@ def _do_override(self, other: Dict[str, Any]): class RegularShards(ConfigPart): - def __init__(self, - num_shards: int, - consensus_size: int, - num_observers_per_shard: int, - num_validators_per_shard: int): + def __init__( + self, + num_shards: int, + consensus_size: int, + num_observers_per_shard: int, + num_validators_per_shard: int, + ): self.num_shards: int = num_shards self.consensus_size: int = consensus_size self.num_observers_per_shard: int = num_observers_per_shard diff --git a/multiversx_sdk_cli/localnet/config_software.py b/multiversx_sdk_cli/localnet/config_software.py index ca8cdf2b..14e5b907 100644 --- a/multiversx_sdk_cli/localnet/config_software.py +++ b/multiversx_sdk_cli/localnet/config_software.py @@ -12,10 +12,7 @@ class SoftwareResolution(Enum): class Software(ConfigPart): - def __init__( - self, - mx_chain_go: 'SoftwareChainGo', - mx_chain_proxy_go: 'SoftwareChainProxyGo'): + def __init__(self, mx_chain_go: "SoftwareChainGo", mx_chain_proxy_go: "SoftwareChainProxyGo"): self.mx_chain_go = mx_chain_go self.mx_chain_proxy_go = mx_chain_proxy_go @@ -34,12 +31,14 @@ def to_dictionary(self) -> Dict[str, Any]: class SoftwareComponent(ConfigPart): - def __init__(self, - resolution: SoftwareResolution, - archive_url: str, - archive_download_folder: Path, - archive_extraction_folder: Path, - local_path: Path): + def __init__( + self, + resolution: SoftwareResolution, + archive_url: str, + archive_download_folder: Path, + archive_extraction_folder: Path, + local_path: Path, + ): self.resolution: SoftwareResolution = resolution self.archive_url: str = archive_url self.archive_download_folder: Path = archive_download_folder @@ -58,10 +57,14 @@ def _do_override(self, other: Dict[str, Any]) -> None: def _verify(self): if self.resolution == SoftwareResolution.Remote: if not self.archive_url: - raise KnownError(f"In configuration section '{self.get_name()}', resolution is '{self.resolution.value}', but 'archive_url' is bad (empty)") + raise KnownError( + f"In configuration section '{self.get_name()}', resolution is '{self.resolution.value}', but 'archive_url' is bad (empty)" + ) if self.resolution == SoftwareResolution.Local: if not self.get_local_path().is_dir(): - raise KnownError(f"In configuration section '{self.get_name()}', resolution is '{self.resolution.value}', but 'local_path' is not a directory: {self.local_path}") + raise KnownError( + f"In configuration section '{self.get_name()}', resolution is '{self.resolution.value}', but 'local_path' is not a directory: {self.local_path}" + ) def get_archive_download_folder(self): return self.archive_download_folder.expanduser().resolve() diff --git a/multiversx_sdk_cli/localnet/constants.py b/multiversx_sdk_cli/localnet/constants.py index 26ef194c..5e5bec4f 100644 --- a/multiversx_sdk_cli/localnet/constants.py +++ b/multiversx_sdk_cli/localnet/constants.py @@ -3,4 +3,6 @@ METACHAIN_ID = 4294967295 NETWORK_MONITORING_INTERVAL_IN_SECONDS = 1 # Read, write and execute by owner, read and execute by group and others -FILE_MODE_EXECUTABLE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH +FILE_MODE_EXECUTABLE = ( + stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH +) diff --git a/multiversx_sdk_cli/localnet/genesis_json.py b/multiversx_sdk_cli/localnet/genesis_json.py index bfd61d63..0b802ed8 100644 --- a/multiversx_sdk_cli/localnet/genesis_json.py +++ b/multiversx_sdk_cli/localnet/genesis_json.py @@ -38,10 +38,7 @@ def _build_validator_entry(nickname: str, account: Account, value: int) -> Dict[ "supply": str(value), "balance": "0", "stakingvalue": str(value), - "delegation": { - "address": "", - "value": "0" - } + "delegation": {"address": "", "value": "0"}, } @@ -52,8 +49,5 @@ def _build_user_entry(nickname: str, account: Account, value: int) -> Dict[str, "supply": str(value), "balance": str(value), "stakingvalue": "0", - "delegation": { - "address": "", - "value": "0" - } + "delegation": {"address": "", "value": "0"}, } diff --git a/multiversx_sdk_cli/localnet/node_config_toml.py b/multiversx_sdk_cli/localnet/node_config_toml.py index 1f073d2d..2493d7fc 100644 --- a/multiversx_sdk_cli/localnet/node_config_toml.py +++ b/multiversx_sdk_cli/localnet/node_config_toml.py @@ -7,59 +7,61 @@ def patch_config(data: ConfigDict, config: ConfigRoot): - data['GeneralSettings']['ChainID'] = CHAIN_ID + data["GeneralSettings"]["ChainID"] = CHAIN_ID # "--operation-mode=historical-balances" is not available for nodes, # since << validator cannot be a full archive node >>, # but we attempt to set the "deep-history" mode as follows: - data['DbLookupExtensions']['Enabled'] = True - data['GeneralSettings']['StartInEpochEnabled'] = False - data['StateTriesConfig']['AccountsStatePruningEnabled'] = False - data['StoragePruning']['ObserverCleanOldEpochsData'] = False - data['StoragePruning']['AccountsTrieCleanOldEpochsData'] = False + data["DbLookupExtensions"]["Enabled"] = True + data["GeneralSettings"]["StartInEpochEnabled"] = False + data["StateTriesConfig"]["AccountsStatePruningEnabled"] = False + data["StoragePruning"]["ObserverCleanOldEpochsData"] = False + data["StoragePruning"]["AccountsTrieCleanOldEpochsData"] = False # Make epochs shorter epoch_start_config: ConfigDict = dict() - epoch_start_config['RoundsPerEpoch'] = config.general.rounds_per_epoch - epoch_start_config['MinRoundsBetweenEpochs'] = int(config.general.rounds_per_epoch / 4) + epoch_start_config["RoundsPerEpoch"] = config.general.rounds_per_epoch + epoch_start_config["MinRoundsBetweenEpochs"] = int(config.general.rounds_per_epoch / 4) - data['EpochStartConfig'].update(epoch_start_config) - data['WebServerAntiflood']['VmQueryDelayAfterStartInSec'] = 30 + data["EpochStartConfig"].update(epoch_start_config) + data["WebServerAntiflood"]["VmQueryDelayAfterStartInSec"] = 30 # Always use the latest VM - data['VirtualMachine']['Execution']['WasmVMVersions'] = [{'StartEpoch': 0, 'Version': '*'}] - data['VirtualMachine']['Querying']['WasmVMVersions'] = [{'StartEpoch': 0, 'Version': '*'}] + data["VirtualMachine"]["Execution"]["WasmVMVersions"] = [{"StartEpoch": 0, "Version": "*"}] + data["VirtualMachine"]["Querying"]["WasmVMVersions"] = [{"StartEpoch": 0, "Version": "*"}] def patch_api(data: ConfigDict, config: ConfigRoot): - routes = data['APIPackages']['transaction']['Routes'] + routes = data["APIPackages"]["transaction"]["Routes"] for route in routes: - route['Open'] = True + route["Open"] = True def patch_enable_epochs(data: ConfigDict, config: ConfigRoot): enable_epochs = data["EnableEpochs"] - enable_epochs['SCDeployEnableEpoch'] = 0 - enable_epochs['BuiltInFunctionsEnableEpoch'] = 0 - enable_epochs['RelayedTransactionsEnableEpoch'] = 0 - enable_epochs['PenalizedTooMuchGasEnableEpoch'] = 0 - enable_epochs['AheadOfTimeGasUsageEnableEpoch'] = 0 - enable_epochs['GasPriceModifierEnableEpoch'] = 0 - enable_epochs['RepairCallbackEnableEpoch'] = 0 - enable_epochs['ReturnDataToLastTransferEnableEpoch'] = 0 - enable_epochs['SenderInOutTransferEnableEpoch'] = 0 - enable_epochs['ESDTEnableEpoch'] = 0 - enable_epochs['IncrementSCRNonceInMultiTransferEnableEpoch'] = 0 - enable_epochs['ESDTMultiTransferEnableEpoch'] = 0 - enable_epochs['GlobalMintBurnDisableEpoch'] = 0 - enable_epochs['ESDTTransferRoleEnableEpoch'] = 0 - enable_epochs['BuiltInFunctionOnMetaEnableEpoch'] = 0 - enable_epochs['MultiESDTTransferFixOnCallBackOnEnableEpoch'] = 0 - enable_epochs['ESDTNFTCreateOnMultiShard'] = 0 - enable_epochs['MetaESDTSetEnableEpoch'] = 0 - enable_epochs['DelegationManagerEnableEpoch'] = 0 + enable_epochs["SCDeployEnableEpoch"] = 0 + enable_epochs["BuiltInFunctionsEnableEpoch"] = 0 + enable_epochs["RelayedTransactionsEnableEpoch"] = 0 + enable_epochs["PenalizedTooMuchGasEnableEpoch"] = 0 + enable_epochs["AheadOfTimeGasUsageEnableEpoch"] = 0 + enable_epochs["GasPriceModifierEnableEpoch"] = 0 + enable_epochs["RepairCallbackEnableEpoch"] = 0 + enable_epochs["ReturnDataToLastTransferEnableEpoch"] = 0 + enable_epochs["SenderInOutTransferEnableEpoch"] = 0 + enable_epochs["ESDTEnableEpoch"] = 0 + enable_epochs["IncrementSCRNonceInMultiTransferEnableEpoch"] = 0 + enable_epochs["ESDTMultiTransferEnableEpoch"] = 0 + enable_epochs["GlobalMintBurnDisableEpoch"] = 0 + enable_epochs["ESDTTransferRoleEnableEpoch"] = 0 + enable_epochs["BuiltInFunctionOnMetaEnableEpoch"] = 0 + enable_epochs["MultiESDTTransferFixOnCallBackOnEnableEpoch"] = 0 + enable_epochs["ESDTNFTCreateOnMultiShard"] = 0 + enable_epochs["MetaESDTSetEnableEpoch"] = 0 + enable_epochs["DelegationManagerEnableEpoch"] = 0 - max_nodes_change_enable_epoch = enable_epochs['MaxNodesChangeEnableEpoch'] + max_nodes_change_enable_epoch = enable_epochs["MaxNodesChangeEnableEpoch"] last_entry = max_nodes_change_enable_epoch[-1] penultimate_entry = max_nodes_change_enable_epoch[-2] - last_entry['MaxNumNodes'] = penultimate_entry['MaxNumNodes'] - (config.shards.num_shards + 1) * penultimate_entry['NodesToShufflePerShard'] + last_entry["MaxNumNodes"] = ( + penultimate_entry["MaxNumNodes"] - (config.shards.num_shards + 1) * penultimate_entry["NodesToShufflePerShard"] + ) diff --git a/multiversx_sdk_cli/localnet/nodes_setup_json.py b/multiversx_sdk_cli/localnet/nodes_setup_json.py index f1570fb6..b99a4c2a 100644 --- a/multiversx_sdk_cli/localnet/nodes_setup_json.py +++ b/multiversx_sdk_cli/localnet/nodes_setup_json.py @@ -22,7 +22,7 @@ def build(config: ConfigRoot) -> Any: # Then, patch the list of initial nodes, so that higher indexes will become metachain nodes. num_metachain_nodes = config.metashard.num_validators num_nodes = len(initial_nodes) - initial_nodes = initial_nodes[num_nodes - num_metachain_nodes:] + initial_nodes[:num_nodes - num_metachain_nodes] + initial_nodes = initial_nodes[num_nodes - num_metachain_nodes :] + initial_nodes[: num_nodes - num_metachain_nodes] return { "startTime": config.genesis_time(), @@ -35,5 +35,5 @@ def build(config: ConfigRoot) -> Any: "adaptivity": False, "chainID": CHAIN_ID, "minTransactionVersion": 1, - "initialNodes": initial_nodes + "initialNodes": initial_nodes, } diff --git a/multiversx_sdk_cli/localnet/p2p_toml.py b/multiversx_sdk_cli/localnet/p2p_toml.py index b4e5a4f4..830d4ca6 100644 --- a/multiversx_sdk_cli/localnet/p2p_toml.py +++ b/multiversx_sdk_cli/localnet/p2p_toml.py @@ -2,23 +2,21 @@ from multiversx_sdk_cli.localnet.config_root import ConfigRoot -PROTOCOL_ID = '/erd/kad/sandbox' +PROTOCOL_ID = "/erd/kad/sandbox" def patch(data: Any, config: ConfigRoot, node_index: int, port_first: int) -> Any: - data['Node']['Port'] = str(port_first + node_index) - data['Node']['ThresholdMinConnectedPeers'] = 1 - data['KadDhtPeerDiscovery']['InitialPeerList'] = [ - config.seednode_address() - ] - data['KadDhtPeerDiscovery']['ProtocolID'] = PROTOCOL_ID - data['Sharding']['Type'] = "NilListSharder" + data["Node"]["Port"] = str(port_first + node_index) + data["Node"]["ThresholdMinConnectedPeers"] = 1 + data["KadDhtPeerDiscovery"]["InitialPeerList"] = [config.seednode_address()] + data["KadDhtPeerDiscovery"]["ProtocolID"] = PROTOCOL_ID + data["Sharding"]["Type"] = "NilListSharder" def patch_for_seednode(data: Any, config: ConfigRoot): port_seednode = config.networking.port_seednode - data['Node']['Port'] = str(port_seednode) - data['Node']['MaximumExpectedPeerCount'] = 16 - data['KadDhtPeerDiscovery']['ProtocolID'] = PROTOCOL_ID - data['Sharding']['Type'] = "NilListSharder" + data["Node"]["Port"] = str(port_seednode) + data["Node"]["MaximumExpectedPeerCount"] = 16 + data["KadDhtPeerDiscovery"]["ProtocolID"] = PROTOCOL_ID + data["Sharding"]["Type"] = "NilListSharder" diff --git a/multiversx_sdk_cli/localnet/step_build_software.py b/multiversx_sdk_cli/localnet/step_build_software.py index 533b0066..7f9b38ca 100644 --- a/multiversx_sdk_cli/localnet/step_build_software.py +++ b/multiversx_sdk_cli/localnet/step_build_software.py @@ -79,12 +79,7 @@ def _set_rpath(cmd_path: Path): return try: - subprocess.check_call([ - "install_name_tool", - "-add_rpath", - "@loader_path", - cmd_path - ]) + subprocess.check_call(["install_name_tool", "-add_rpath", "@loader_path", cmd_path]) except Exception as e: # In most cases, this isn't critical (libraries might be found among the downloaded Go packages). logger.warning(f"Failed to set rpath of {cmd_path}: {e}") diff --git a/multiversx_sdk_cli/localnet/step_config.py b/multiversx_sdk_cli/localnet/step_config.py index 48053a63..d864d5cb 100644 --- a/multiversx_sdk_cli/localnet/step_config.py +++ b/multiversx_sdk_cli/localnet/step_config.py @@ -6,10 +6,15 @@ import multiversx_sdk_cli.utils as utils from multiversx_sdk_cli.errors import KnownError -from multiversx_sdk_cli.localnet import (genesis_json, - genesis_smart_contracts_json, - libraries, node_config_toml, - nodes_setup_json, p2p_toml, wallets) +from multiversx_sdk_cli.localnet import ( + genesis_json, + genesis_smart_contracts_json, + libraries, + node_config_toml, + nodes_setup_json, + p2p_toml, + wallets, +) from multiversx_sdk_cli.localnet.config_root import ConfigRoot logger = logging.getLogger("localnet") @@ -52,14 +57,8 @@ def configure(configfile: Path): config, config.observer_config_folders(), ) - overwrite_genesis_file( - config, - config.validator_config_folders() - ) - overwrite_genesis_file( - config, - config.observer_config_folders() - ) + overwrite_genesis_file(config, config.validator_config_folders()) + overwrite_genesis_file(config, config.observer_config_folders()) # Seed node copy_config_to_seednode(config) @@ -78,7 +77,7 @@ def create_folders(config: ConfigRoot): folder = config.proxy_folder() makefolder(folder) - makefolder(folder / 'config') + makefolder(folder / "config") for folder in config.all_nodes_folders(): makefolder(folder) @@ -101,22 +100,22 @@ def copy_validator_keys(config: ConfigRoot): def patch_node_config(config: ConfigRoot): for node_config in config.all_nodes_config_folders(): - node_config_file = node_config / 'config.toml' + node_config_file = node_config / "config.toml" data = utils.read_toml_file(node_config_file) node_config_toml.patch_config(data, config) utils.write_toml_file(node_config_file, data) - api_config_file = node_config / 'api.toml' + api_config_file = node_config / "api.toml" data = utils.read_toml_file(api_config_file) node_config_toml.patch_api(data, config) utils.write_toml_file(api_config_file, data) - enable_epochs_config_file = node_config / 'enableEpochs.toml' + enable_epochs_config_file = node_config / "enableEpochs.toml" data = utils.read_toml_file(enable_epochs_config_file) node_config_toml.patch_enable_epochs(data, config) utils.write_toml_file(enable_epochs_config_file, data) - genesis_smart_contracts_file = node_config / 'genesisSmartContracts.json' + genesis_smart_contracts_file = node_config / "genesisSmartContracts.json" data = utils.read_json_file(genesis_smart_contracts_file) genesis_smart_contracts_json.patch(data, config) utils.write_json_file(genesis_smart_contracts_file, data) @@ -126,13 +125,13 @@ def copy_config_to_seednode(config: ConfigRoot): config_source = config.software.mx_chain_go.get_seednode_config_folder() seednode_config = config.seednode_config_folder() makefolder(seednode_config) - shutil.copy(config_source / 'p2p.toml', seednode_config / 'p2p.toml') - shutil.copy(config_source / 'config.toml', seednode_config / 'config.toml') + shutil.copy(config_source / "p2p.toml", seednode_config / "p2p.toml") + shutil.copy(config_source / "config.toml", seednode_config / "config.toml") def patch_seednode_p2p_config(config: ConfigRoot): seednode_config = config.seednode_config_folder() - seednode_config_file = seednode_config / 'p2p.toml' + seednode_config_file = seednode_config / "p2p.toml" data = utils.read_toml_file(seednode_config_file) p2p_toml.patch_for_seednode(data, config) @@ -146,7 +145,7 @@ def copy_seednode_p2p_key(config: ConfigRoot): def patch_nodes_p2p_config(config: ConfigRoot, nodes_config_folders: List[Path], port_first: int): for index, config_folder in enumerate(nodes_config_folders): - config_file = config_folder / 'p2p.toml' + config_file = config_folder / "p2p.toml" data = utils.read_toml_file(config_file) p2p_toml.patch(data, config, index, port_first) utils.write_toml_file(config_file, data) @@ -156,14 +155,14 @@ def overwrite_nodes_setup(config: ConfigRoot, nodes_config_folders: List[Path]): nodes_setup = nodes_setup_json.build(config) for _, config_folder in enumerate(nodes_config_folders): - utils.write_json_file(config_folder / 'nodesSetup.json', nodes_setup) + utils.write_json_file(config_folder / "nodesSetup.json", nodes_setup) def overwrite_genesis_file(config: ConfigRoot, nodes_config_folders: List[Path]): genesis = genesis_json.build(config) for _, config_folder in enumerate(nodes_config_folders): - utils.write_json_file(config_folder / 'genesis.json', genesis) + utils.write_json_file(config_folder / "genesis.json", genesis) def copy_config_to_proxy(config: ConfigRoot): @@ -171,27 +170,27 @@ def copy_config_to_proxy(config: ConfigRoot): proxy_config = config.proxy_config_folder() makefolder(proxy_config) - shutil.copy(config_prototype / 'config.toml', proxy_config) - shutil.copytree(config_prototype / 'apiConfig', proxy_config / 'apiConfig') + shutil.copy(config_prototype / "config.toml", proxy_config) + shutil.copytree(config_prototype / "apiConfig", proxy_config / "apiConfig") # Removed in newer versions: # https://github.com/multiversx/mx-chain-proxy-go/pull/454 - if (config_prototype / 'external.toml').exists(): - shutil.copy(config_prototype / 'external.toml', proxy_config) + if (config_prototype / "external.toml").exists(): + shutil.copy(config_prototype / "external.toml", proxy_config) def patch_proxy_config(config: ConfigRoot): - proxy_config_file = config.proxy_config_folder() / 'config.toml' + proxy_config_file = config.proxy_config_folder() / "config.toml" nodes = config.api_addresses_sharded_for_proxy_config() data = utils.read_toml_file(proxy_config_file) - data['Observers'] = nodes - data['FullHistoryNodes'] = nodes - data['GeneralSettings']['ServerPort'] = config.networking.port_proxy + data["Observers"] = nodes + data["FullHistoryNodes"] = nodes + data["GeneralSettings"]["ServerPort"] = config.networking.port_proxy utils.write_toml_file(proxy_config_file, data) - api_config_file = path.join(config.proxy_config_folder(), 'apiConfig', 'v1_0.toml') + api_config_file = path.join(config.proxy_config_folder(), "apiConfig", "v1_0.toml") data = utils.read_toml_file(api_config_file) - routes = data['APIPackages']['transaction']['Routes'] + routes = data["APIPackages"]["transaction"]["Routes"] for route in routes: route["Open"] = True utils.write_toml_file(api_config_file, data) diff --git a/multiversx_sdk_cli/localnet/step_prerequisites.py b/multiversx_sdk_cli/localnet/step_prerequisites.py index bff27822..2ce69773 100644 --- a/multiversx_sdk_cli/localnet/step_prerequisites.py +++ b/multiversx_sdk_cli/localnet/step_prerequisites.py @@ -5,8 +5,10 @@ from multiversx_sdk_cli import dependencies from multiversx_sdk_cli.localnet.config_root import ConfigRoot -from multiversx_sdk_cli.localnet.config_software import (SoftwareComponent, - SoftwareResolution) +from multiversx_sdk_cli.localnet.config_software import ( + SoftwareComponent, + SoftwareResolution, +) logger = logging.getLogger("localnet") diff --git a/multiversx_sdk_cli/localnet/step_start.py b/multiversx_sdk_cli/localnet/step_start.py index c061aacf..0a217722 100644 --- a/multiversx_sdk_cli/localnet/step_start.py +++ b/multiversx_sdk_cli/localnet/step_start.py @@ -11,8 +11,7 @@ from multiversx_sdk_cli import workstation from multiversx_sdk_cli.localnet.config_root import ConfigRoot -from multiversx_sdk_cli.localnet.constants import \ - NETWORK_MONITORING_INTERVAL_IN_SECONDS +from multiversx_sdk_cli.localnet.constants import NETWORK_MONITORING_INTERVAL_IN_SECONDS logger = logging.getLogger("localnet") @@ -40,49 +39,69 @@ async def do_start(configfile: Path, stop_after_seconds: int): display_api_table(config) - logger.info('Localnet folder is %s', config.root()) + logger.info("Localnet folder is %s", config.root()) to_run: List[Coroutine[Any, Any, None]] = [] # Seed node - to_run.append(run([ - "./seednode", - "--log-save", - f"--rest-api-interface={config.seednode_api_interface()}", - ], cwd=config.seednode_folder())) + to_run.append( + run( + [ + "./seednode", + "--log-save", + f"--rest-api-interface={config.seednode_api_interface()}", + ], + cwd=config.seednode_folder(), + ) + ) loglevel = _patch_loglevel(config.general.log_level) logger.info(f"loglevel: {loglevel}") # Observers for observer in config.observers(): - to_run.append(run([ - "./node", - "--use-log-view", - "--log-save", - f"--log-level={loglevel}", - "--log-logger-name", - f"--destination-shard-as-observer={observer.shard}", - f"--rest-api-interface={observer.api_interface()}", - "--operation-mode=historical-balances" - ], cwd=observer.folder, delay=NODES_START_DELAY)) + to_run.append( + run( + [ + "./node", + "--use-log-view", + "--log-save", + f"--log-level={loglevel}", + "--log-logger-name", + f"--destination-shard-as-observer={observer.shard}", + f"--rest-api-interface={observer.api_interface()}", + "--operation-mode=historical-balances", + ], + cwd=observer.folder, + delay=NODES_START_DELAY, + ) + ) # Validators for validator in config.validators(): - to_run.append(run([ - "./node", - "--use-log-view", - "--log-save", - f"--log-level={loglevel}", - "--log-logger-name", - f"--rest-api-interface={validator.api_interface()}" - ], cwd=validator.folder, delay=NODES_START_DELAY)) + to_run.append( + run( + [ + "./node", + "--use-log-view", + "--log-save", + f"--log-level={loglevel}", + "--log-logger-name", + f"--rest-api-interface={validator.api_interface()}", + ], + cwd=validator.folder, + delay=NODES_START_DELAY, + ) + ) # Proxy - to_run.append(run([ - "./proxy", - "--log-save" - ], cwd=config.proxy_folder(), delay=PROXY_START_DELAY)) + to_run.append( + run( + ["./proxy", "--log-save"], + cwd=config.proxy_folder(), + delay=PROXY_START_DELAY, + ) + ) # Monitor network to_run.append(monitor_network(stop_after_seconds)) @@ -139,16 +158,24 @@ async def run(args: List[str], cwd: Path, delay: int = 0): # For MacOS, dylibs are directly found near the binary (no workaround needed) pass - process = await asyncio.create_subprocess_exec(*args, stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, cwd=cwd, limit=1024 * 512, env=env) + process = await asyncio.create_subprocess_exec( + *args, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + cwd=cwd, + limit=1024 * 512, + env=env, + ) pid = process.pid print(f"Started process [{pid}]", args) - await asyncio.wait([ - asyncio.create_task(_read_stream(process.stdout, pid)), - asyncio.create_task(_read_stream(process.stderr, pid)) - ]) + await asyncio.wait( + [ + asyncio.create_task(_read_stream(process.stdout, pid)), + asyncio.create_task(_read_stream(process.stderr, pid)), + ] + ) return_code = await process.wait() print(f"Proces [{pid}] stopped. Return code: {return_code}.") @@ -180,7 +207,13 @@ def _patch_loglevel(loglevel: str) -> str: LOGLINE_GENESIS_THRESHOLD_MARKER = "started committing block" -LOGLINE_AFTER_GENESIS_INTERESTING_MARKERS = ["started committing block", "ERROR", "WARN", "vm", "smartcontract"] +LOGLINE_AFTER_GENESIS_INTERESTING_MARKERS = [ + "started committing block", + "ERROR", + "WARN", + "vm", + "smartcontract", +] # We ignore SC calls on genesis. LOGLINE_ON_GENESIS_INTERESTING_MARKERS = ["started committing block", "ERROR", "WARN"] diff --git a/multiversx_sdk_cli/myprocess.py b/multiversx_sdk_cli/myprocess.py index d2bcb587..a8a72277 100644 --- a/multiversx_sdk_cli/myprocess.py +++ b/multiversx_sdk_cli/myprocess.py @@ -8,11 +8,23 @@ logger = logging.getLogger("myprocess") -def run_process(args: List[str], env: Any = None, dump_to_stdout: bool = True, cwd: Optional[Union[str, Path]] = None) -> str: +def run_process( + args: List[str], + env: Any = None, + dump_to_stdout: bool = True, + cwd: Optional[Union[str, Path]] = None, +) -> str: logger.info(f"run_process: {args}, in folder: {cwd}") try: - output = subprocess.check_output(args, shell=False, universal_newlines=True, stderr=subprocess.STDOUT, env=env, cwd=cwd) + output = subprocess.check_output( + args, + shell=False, + universal_newlines=True, + stderr=subprocess.STDOUT, + env=env, + cwd=cwd, + ) logger.info("Successful run. Output:") if dump_to_stdout: print(output or "[No output]") diff --git a/multiversx_sdk_cli/native_auth_client.py b/multiversx_sdk_cli/native_auth_client.py index f7174b8e..8979057a 100644 --- a/multiversx_sdk_cli/native_auth_client.py +++ b/multiversx_sdk_cli/native_auth_client.py @@ -12,13 +12,13 @@ class NativeAuthClientConfig: def __init__( - self, - origin: str = '', - api_url: str = DEFAULT_API_URL, - expiry_seconds: int = DEFAULT_EXPIRY_TIME_IN_SECONDS, - block_hash_shard: Optional[int] = None, - gateway_url: Optional[str] = None, - extra_request_headers: Optional[Dict[str, str]] = None + self, + origin: str = "", + api_url: str = DEFAULT_API_URL, + expiry_seconds: int = DEFAULT_EXPIRY_TIME_IN_SECONDS, + block_hash_shard: Optional[int] = None, + gateway_url: Optional[str] = None, + extra_request_headers: Optional[Dict[str, str]] = None, ) -> None: self.origin = origin self.api_url = api_url @@ -92,7 +92,7 @@ def _get_current_block_hash_using_api_fallback(self) -> str: return response[0]["hash"] def _encode_value(self, string: str) -> str: - encoded = base64.b64encode(string.encode('utf-8')).decode('utf-8') + encoded = base64.b64encode(string.encode("utf-8")).decode("utf-8") return self._escape(encoded) def _escape(self, string: str) -> str: diff --git a/multiversx_sdk_cli/projects/__init__.py b/multiversx_sdk_cli/projects/__init__.py index db23619f..791f43b6 100644 --- a/multiversx_sdk_cli/projects/__init__.py +++ b/multiversx_sdk_cli/projects/__init__.py @@ -1,8 +1,21 @@ -from multiversx_sdk_cli.projects.core import (build_project, clean_project, - load_project, run_tests) +from multiversx_sdk_cli.projects.core import ( + build_project, + clean_project, + load_project, + run_tests, +) from multiversx_sdk_cli.projects.project_base import Project from multiversx_sdk_cli.projects.project_rust import ProjectRust from multiversx_sdk_cli.projects.report.do_report import do_report from multiversx_sdk_cli.projects.templates import Contract -__all__ = ["build_project", "clean_project", "do_report", "run_tests", "load_project", "Project", "ProjectRust", "Contract"] +__all__ = [ + "build_project", + "clean_project", + "do_report", + "run_tests", + "load_project", + "Project", + "ProjectRust", + "Contract", +] diff --git a/multiversx_sdk_cli/projects/core.py b/multiversx_sdk_cli/projects/core.py index 1a663731..fbcfba3f 100644 --- a/multiversx_sdk_cli/projects/core.py +++ b/multiversx_sdk_cli/projects/core.py @@ -4,8 +4,10 @@ from multiversx_sdk_cli import errors, guards from multiversx_sdk_cli.projects import shared -from multiversx_sdk_cli.projects.constants import (OLD_PROJECT_CONFIG_FILENAME, - PROJECT_CONFIG_FILENAME) +from multiversx_sdk_cli.projects.constants import ( + OLD_PROJECT_CONFIG_FILENAME, + PROJECT_CONFIG_FILENAME, +) from multiversx_sdk_cli.projects.project_base import Project from multiversx_sdk_cli.projects.project_rust import ProjectRust diff --git a/multiversx_sdk_cli/projects/interfaces.py b/multiversx_sdk_cli/projects/interfaces.py index 980b7b48..3a14fa5d 100644 --- a/multiversx_sdk_cli/projects/interfaces.py +++ b/multiversx_sdk_cli/projects/interfaces.py @@ -2,7 +2,7 @@ from typing import Any -class IProject(): +class IProject: def get_option(self, option_name: str) -> Any: return None diff --git a/multiversx_sdk_cli/projects/migrations.py b/multiversx_sdk_cli/projects/migrations.py index 6f861011..b0ddda1a 100644 --- a/multiversx_sdk_cli/projects/migrations.py +++ b/multiversx_sdk_cli/projects/migrations.py @@ -1,7 +1,9 @@ from pathlib import Path -from multiversx_sdk_cli.projects.constants import (OLD_PROJECT_CONFIG_FILENAME, - PROJECT_CONFIG_FILENAME) +from multiversx_sdk_cli.projects.constants import ( + OLD_PROJECT_CONFIG_FILENAME, + PROJECT_CONFIG_FILENAME, +) def migrate_project_config_file(project_path: Path): diff --git a/multiversx_sdk_cli/projects/project_base.py b/multiversx_sdk_cli/projects/project_base.py index 75d7db8b..dd1a2e8a 100644 --- a/multiversx_sdk_cli/projects/project_base.py +++ b/multiversx_sdk_cli/projects/project_base.py @@ -173,7 +173,7 @@ def get_contract_suffix(name: str) -> str: def remove_suffix(name: str, suffix: str) -> str: if not name.endswith(suffix) or len(suffix) == 0: return name - return name[:-len(suffix)] + return name[: -len(suffix)] def adjust_wasm_filename(path: Path, name_hint: str) -> Path: diff --git a/multiversx_sdk_cli/projects/project_rust.py b/multiversx_sdk_cli/projects/project_rust.py index 7825d5d6..fba6849e 100644 --- a/multiversx_sdk_cli/projects/project_rust.py +++ b/multiversx_sdk_cli/projects/project_rust.py @@ -18,21 +18,15 @@ def __init__(self, directory: Path): def clean(self): env = self.get_env() - args = [ - "sc-meta", - "all", - "clean", - "--path", - self.directory - ] + args = ["sc-meta", "all", "clean", "--path", self.directory] subprocess.check_call(args, env=env) def get_meta_folder(self): - return self.path / 'meta' + return self.path / "meta" def get_wasm_view_folder(self): - return self.path / 'wasm-view' + return self.path / "wasm-view" def perform_build(self): meta = self.has_meta() @@ -42,21 +36,19 @@ def perform_build(self): self.run_meta() def prepare_build_wasm_args(self, args: List[str]): - args.extend([ - "--target=wasm32-unknown-unknown", - "--release", - "--out-dir", - self.get_output_folder() - ]) + args.extend( + [ + "--target=wasm32-unknown-unknown", + "--release", + "--out-dir", + self.get_output_folder(), + ] + ) def run_meta(self): env = self.get_env() - args = [ - "sc-meta", - "all", - "build" - ] + args = ["sc-meta", "all", "build"] args.extend(self.forwarded_args) @@ -112,9 +104,11 @@ def build_wasm_with_debug_symbols(self, build_options: Dict[str, Any]): "all", "build", "--wasm-symbols", - "--wasm-suffix", "dbg", + "--wasm-suffix", + "dbg", "--no-wasm-opt", - "--target-dir", target_dir + "--target-dir", + target_dir, ] try: diff --git a/multiversx_sdk_cli/projects/report/data/common.py b/multiversx_sdk_cli/projects/report/data/common.py index e2983c6e..a306ce64 100644 --- a/multiversx_sdk_cli/projects/report/data/common.py +++ b/multiversx_sdk_cli/projects/report/data/common.py @@ -1,5 +1,5 @@ -from collections import OrderedDict import itertools +from collections import OrderedDict from typing import Callable, List, Optional, TypeVar @@ -11,8 +11,8 @@ def merge_values(first: List[str], second: List[str]) -> List[str]: return list(OrderedDict.fromkeys(first + second)) -T = TypeVar('T') -K = TypeVar('K') +T = TypeVar("T") +K = TypeVar("K") def first_not_none(first: Optional[T], second: Optional[T]) -> T: @@ -23,26 +23,31 @@ def get_keys(items: List[T], key_getter: Callable[[T], K]) -> List[K]: return [key_getter(item) for item in items] -def list_as_key_value_dict(items: List[T], key_getter: Callable[[T], K]) -> 'OrderedDict[K, T]': +def list_as_key_value_dict(items: List[T], key_getter: Callable[[T], K]) -> "OrderedDict[K, T]": return OrderedDict(zip(get_keys(items, key_getter), items)) -def merge_values_by_key(first: List[T], second: List[T], key_getter: Callable[[T], K], merge: Callable[[Optional[T], Optional[T]], T]) -> List[T]: +def merge_values_by_key( + first: List[T], + second: List[T], + key_getter: Callable[[T], K], + merge: Callable[[Optional[T], Optional[T]], T], +) -> List[T]: """ - Merge the values of two lists when the key matches. - Used in order to de-duplicate report entries depending on certain criteria, such as paths or feature names. - ->>> def merge_func(a, b): -... if a == None: -... return (b[0], b[1] + 100) -... if b == None: -... return (a[0], a[1] + 200) -... return (a[0], a[1] + b[1]) ->>> first = [('one', 1), ('two', 2)] ->>> second = [('two', 3), ('three', 4)] ->>> key_getter = lambda item: item[0] ->>> merge_values_by_key(first, second, key_getter, merge_func) -[('one', 201), ('two', 5), ('three', 104)] + Merge the values of two lists when the key matches. + Used in order to de-duplicate report entries depending on certain criteria, such as paths or feature names. + + >>> def merge_func(a, b): + ... if a == None: + ... return (b[0], b[1] + 100) + ... if b == None: + ... return (a[0], a[1] + 200) + ... return (a[0], a[1] + b[1]) + >>> first = [('one', 1), ('two', 2)] + >>> second = [('two', 3), ('three', 4)] + >>> key_getter = lambda item: item[0] + >>> merge_values_by_key(first, second, key_getter, merge_func) + [('one', 201), ('two', 5), ('three', 104)] """ first_as_dict = list_as_key_value_dict(first, key_getter) second_as_dict = list_as_key_value_dict(second, key_getter) diff --git a/multiversx_sdk_cli/projects/report/data/extracted_feature.py b/multiversx_sdk_cli/projects/report/data/extracted_feature.py index 02ed3d4f..724f15ac 100644 --- a/multiversx_sdk_cli/projects/report/data/extracted_feature.py +++ b/multiversx_sdk_cli/projects/report/data/extracted_feature.py @@ -1,6 +1,9 @@ from typing import Any, List, Optional -from multiversx_sdk_cli.projects.report.data.common import first_not_none, merge_values_by_key +from multiversx_sdk_cli.projects.report.data.common import ( + first_not_none, + merge_values_by_key, +) from multiversx_sdk_cli.projects.report.format.change_type import ChangeType from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions @@ -11,23 +14,20 @@ def __init__(self, feature_name: str, results: List[str]) -> None: self.results = results def to_json(self) -> Any: - return { - 'feature_name': self.feature_name, - 'results': self.results - } + return {"feature_name": self.feature_name, "results": self.results} @staticmethod - def from_json(json: Any) -> 'ExtractedFeature': - return ExtractedFeature(json['feature_name'], json['results']) + def from_json(json: Any) -> "ExtractedFeature": + return ExtractedFeature(json["feature_name"], json["results"]) def results_to_markdown(self, format_options: FormatOptions) -> str: - separator = ' :arrow_right: ' if format_options.github_flavor else ' -> ' + separator = " :arrow_right: " if format_options.github_flavor else " -> " change_type = self._classify_changes() display_results = _prepare_results_for_markdown(self.results) if change_type == ChangeType.NONE: return display_results[0] else: - return separator.join(display_results) + ' ' + change_type.to_markdown(format_options) + return separator.join(display_results) + " " + change_type.to_markdown(format_options) def _classify_changes(self) -> ChangeType: """ @@ -41,14 +41,14 @@ def _classify_changes(self) -> ChangeType: all_results_are_identical = all(result == self.results[0] for result in self.results) if all_results_are_identical: return ChangeType.NONE - any_is_not_available = any(result == 'N/A' for result in self.results) + any_is_not_available = any(result == "N/A" for result in self.results) if any_is_not_available: return ChangeType.UNKNOWN - if self.feature_name == 'size': + if self.feature_name == "size": sizes = list(map(int, self.results)) return _classify_list(sizes, reverse=True) - if self.feature_name == 'has-allocator' or self.feature_name == 'has-format': - presence_checks = list(map(lambda value: False if value == 'False' else True, self.results)) + if self.feature_name == "has-allocator" or self.feature_name == "has-format": + presence_checks = list(map(lambda value: False if value == "False" else True, self.results)) return _classify_list(presence_checks, reverse=True) return ChangeType.UNKNOWN @@ -58,10 +58,10 @@ def _prepare_results_for_markdown(results: List[str]) -> List[str]: def _replace_bool_with_yes_no(item: str) -> str: - if item == 'True': - return 'Yes' - if item == 'False': - return 'No' + if item == "True": + return "Yes" + if item == "False": + return "No" return item @@ -83,7 +83,9 @@ def _is_strictly_worse(items: List[Any]) -> bool: return sorted(items, reverse=True) == items -def merge_lists_of_extracted_features(first: List[ExtractedFeature], second: List[ExtractedFeature]) -> List[ExtractedFeature]: +def merge_lists_of_extracted_features( + first: List[ExtractedFeature], second: List[ExtractedFeature] +) -> List[ExtractedFeature]: return merge_values_by_key(first, second, _get_extracted_feature_key, _merge_two_extracted_features) @@ -91,7 +93,9 @@ def _get_extracted_feature_key(extracted_feature: ExtractedFeature) -> str: return extracted_feature.feature_name -def _merge_two_extracted_features(first: Optional[ExtractedFeature], second: Optional[ExtractedFeature]) -> ExtractedFeature: +def _merge_two_extracted_features( + first: Optional[ExtractedFeature], second: Optional[ExtractedFeature] +) -> ExtractedFeature: any = first_not_none(first, second) merged_results = _results_or_NA(first) + _results_or_NA(second) return ExtractedFeature(any.feature_name, merged_results) @@ -99,6 +103,6 @@ def _merge_two_extracted_features(first: Optional[ExtractedFeature], second: Opt def _results_or_NA(extracted_feature: Optional[ExtractedFeature]) -> List[str]: if extracted_feature is None: - return ['N/A'] + return ["N/A"] else: return extracted_feature.results diff --git a/multiversx_sdk_cli/projects/report/data/folder_report.py b/multiversx_sdk_cli/projects/report/data/folder_report.py index d0559379..a9d10984 100644 --- a/multiversx_sdk_cli/projects/report/data/folder_report.py +++ b/multiversx_sdk_cli/projects/report/data/folder_report.py @@ -2,11 +2,15 @@ from typing import Any, List, Optional from multiversx_sdk_cli.projects.report.data.common import ( - first_not_none, flatten_list_of_rows, merge_values_by_key) + first_not_none, + flatten_list_of_rows, + merge_values_by_key, +) from multiversx_sdk_cli.projects.report.data.project_report import ( - ProjectReport, merge_list_of_projects) -from multiversx_sdk_cli.projects.report.format.format_options import \ - FormatOptions + ProjectReport, + merge_list_of_projects, +) +from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions class FolderReport: @@ -15,15 +19,12 @@ def __init__(self, root_path: Path, projects: List[ProjectReport]) -> None: self.projects = projects def to_json(self) -> Any: - return { - 'root_path': str(self.root_path), - 'projects': self.projects - } + return {"root_path": str(self.root_path), "projects": self.projects} @staticmethod - def from_json(json: Any) -> 'FolderReport': - projects = [ProjectReport.from_json(project) for project in json['projects']] - return FolderReport(Path(json['root_path']), projects) + def from_json(json: Any) -> "FolderReport": + projects = [ProjectReport.from_json(project) for project in json["projects"]] + return FolderReport(Path(json["root_path"]), projects) def get_markdown_rows(self, format_options: FormatOptions) -> List[List[str]]: folder_row = [str(self.root_path)] diff --git a/multiversx_sdk_cli/projects/report/data/project_report.py b/multiversx_sdk_cli/projects/report/data/project_report.py index db137b6a..852efe18 100644 --- a/multiversx_sdk_cli/projects/report/data/project_report.py +++ b/multiversx_sdk_cli/projects/report/data/project_report.py @@ -1,8 +1,14 @@ from pathlib import Path from typing import Any, List, Optional -from multiversx_sdk_cli.projects.report.data.common import first_not_none, merge_values_by_key -from multiversx_sdk_cli.projects.report.data.wasm_report import WasmReport, merge_list_of_wasm_reports +from multiversx_sdk_cli.projects.report.data.common import ( + first_not_none, + merge_values_by_key, +) +from multiversx_sdk_cli.projects.report.data.wasm_report import ( + WasmReport, + merge_list_of_wasm_reports, +) from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions @@ -13,24 +19,30 @@ def __init__(self, project_path: Path, wasm_reports: List[WasmReport]) -> None: def to_json(self) -> Any: return { - 'project_path': str(self.project_path), - 'wasm_reports': self.wasm_reports + "project_path": str(self.project_path), + "wasm_reports": self.wasm_reports, } @staticmethod - def from_json(json: Any) -> 'ProjectReport': - wasm_reports = [WasmReport.from_json(wasm_report) for wasm_report in json['wasm_reports']] - return ProjectReport(Path(json['project_path']), wasm_reports) + def from_json(json: Any) -> "ProjectReport": + wasm_reports = [WasmReport.from_json(wasm_report) for wasm_report in json["wasm_reports"]] + return ProjectReport(Path(json["project_path"]), wasm_reports) def get_rows_markdown(self, format_options: FormatOptions) -> List[List[str]]: wasm_count = len(self.wasm_reports) if wasm_count == 0: return [[f" - {str(self.project_path)} "]] elif wasm_count == 1: - return [[f" - {str(self.project_path / self.wasm_reports[0].wasm_name)}"] + self.wasm_reports[0].get_extracted_features_markdown(format_options)] + return [ + [f" - {str(self.project_path / self.wasm_reports[0].wasm_name)}"] + + self.wasm_reports[0].get_extracted_features_markdown(format_options) + ] else: project_path_row = [f" - {str(self.project_path)}"] - wasm_rows = [[f" - - {wasm.wasm_name}"] + wasm.get_extracted_features_markdown(format_options) for wasm in self.wasm_reports] + wasm_rows = [ + [f" - - {wasm.wasm_name}"] + wasm.get_extracted_features_markdown(format_options) + for wasm in self.wasm_reports + ] return [project_path_row] + wasm_rows @@ -42,7 +54,9 @@ def _get_project_report_path(project_report: ProjectReport) -> Path: return project_report.project_path -def _wasm_reports_or_default(project_report: Optional[ProjectReport]) -> List[WasmReport]: +def _wasm_reports_or_default( + project_report: Optional[ProjectReport], +) -> List[WasmReport]: if project_report is None: return [] return project_report.wasm_reports diff --git a/multiversx_sdk_cli/projects/report/data/report.py b/multiversx_sdk_cli/projects/report/data/report.py index cd9440a6..87f5f5f6 100644 --- a/multiversx_sdk_cli/projects/report/data/report.py +++ b/multiversx_sdk_cli/projects/report/data/report.py @@ -1,11 +1,17 @@ import functools -from io import StringIO import json +from io import StringIO from pathlib import Path from typing import Any, List -from multiversx_sdk_cli.projects.report.data.folder_report import FolderReport, merge_list_of_folder_reports -from multiversx_sdk_cli.projects.report.data.common import flatten_list_of_rows, merge_values +from multiversx_sdk_cli.projects.report.data.common import ( + flatten_list_of_rows, + merge_values, +) +from multiversx_sdk_cli.projects.report.data.folder_report import ( + FolderReport, + merge_list_of_folder_reports, +) from multiversx_sdk_cli.projects.report.format.change_type import ChangeType from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions @@ -16,19 +22,16 @@ def __init__(self, feature_names: List[str], folders: List[FolderReport]) -> Non self.folders = folders def to_json(self) -> Any: - return { - 'features': self.feature_names, - 'folders': self.folders - } + return {"features": self.feature_names, "folders": self.folders} @staticmethod - def from_json(json: Any) -> 'Report': - folders = [FolderReport.from_json(folder_report) for folder_report in json['folders']] - return Report(json['features'], folders) + def from_json(json: Any) -> "Report": + folders = [FolderReport.from_json(folder_report) for folder_report in json["folders"]] + return Report(json["features"], folders) @staticmethod - def load_from_file(report_json_path: Path) -> 'Report': - with open(report_json_path, 'r') as report_file: + def load_from_file(report_json_path: Path) -> "Report": + with open(report_json_path, "r") as report_file: report_json = json.load(report_file) return Report.from_json(report_json) @@ -62,7 +65,7 @@ def to_json_string(self) -> str: def _adjust_table_headers(table_headers: List[str], format_options: FormatOptions) -> None: if not format_options.github_flavor: return - NBSP = '\u00A0' + NBSP = "\u00a0" table_headers[0] = table_headers[0].ljust(60, NBSP) table_headers[1] = table_headers[1].rjust(40, NBSP) table_headers[2] = table_headers[2].rjust(30, NBSP) @@ -92,7 +95,7 @@ def _justify_text_string(string: str, width: int) -> str: def _format_row_markdown(row: List[str], format_options: FormatOptions) -> str: - row += [''] * (4 - len(row)) + row += [""] * (4 - len(row)) if not format_options.github_flavor: row[0] = row[0].ljust(100) row[1] = _justify_text_string(row[1], 20) @@ -104,4 +107,4 @@ def _format_row_markdown(row: List[str], format_options: FormatOptions) -> str: def _write_markdown_row(string: StringIO, row: List[str], format_options: FormatOptions): string.write(_format_row_markdown(row, format_options)) - string.write('\n') + string.write("\n") diff --git a/multiversx_sdk_cli/projects/report/data/wasm_report.py b/multiversx_sdk_cli/projects/report/data/wasm_report.py index 14dcd731..e3cc9dd7 100644 --- a/multiversx_sdk_cli/projects/report/data/wasm_report.py +++ b/multiversx_sdk_cli/projects/report/data/wasm_report.py @@ -1,6 +1,13 @@ from typing import Any, List, Optional -from multiversx_sdk_cli.projects.report.data.common import first_not_none, merge_values_by_key -from multiversx_sdk_cli.projects.report.data.extracted_feature import ExtractedFeature, merge_lists_of_extracted_features + +from multiversx_sdk_cli.projects.report.data.common import ( + first_not_none, + merge_values_by_key, +) +from multiversx_sdk_cli.projects.report.data.extracted_feature import ( + ExtractedFeature, + merge_lists_of_extracted_features, +) from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions @@ -11,14 +18,16 @@ def __init__(self, wasm_name: str, extracted_features: List[ExtractedFeature]) - def to_json(self) -> Any: return { - 'wasm_name': self.wasm_name, - 'extracted_features': self.extracted_features + "wasm_name": self.wasm_name, + "extracted_features": self.extracted_features, } @staticmethod - def from_json(json: Any) -> 'WasmReport': - extracted_features = [ExtractedFeature.from_json(extracted_feature) for extracted_feature in json['extracted_features']] - return WasmReport(json['wasm_name'], extracted_features) + def from_json(json: Any) -> "WasmReport": + extracted_features = [ + ExtractedFeature.from_json(extracted_feature) for extracted_feature in json["extracted_features"] + ] + return WasmReport(json["wasm_name"], extracted_features) def get_extracted_features_markdown(self, format_options: FormatOptions) -> List[str]: return [extracted_feature.results_to_markdown(format_options) for extracted_feature in self.extracted_features] @@ -32,7 +41,9 @@ def _get_wasm_report_key(wasm_report: WasmReport) -> str: return wasm_report.wasm_name -def _get_extracted_features_or_default(wasm: Optional[WasmReport]) -> List[ExtractedFeature]: +def _get_extracted_features_or_default( + wasm: Optional[WasmReport], +) -> List[ExtractedFeature]: if wasm is None: return [] return wasm.extracted_features diff --git a/multiversx_sdk_cli/projects/report/do_report.py b/multiversx_sdk_cli/projects/report/do_report.py index 8d5b2fc0..a0379331 100644 --- a/multiversx_sdk_cli/projects/report/do_report.py +++ b/multiversx_sdk_cli/projects/report/do_report.py @@ -5,12 +5,11 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.projects.core import get_project_paths_recursively -from multiversx_sdk_cli.projects.report.data.report import ( - Report, merge_list_of_reports) -from multiversx_sdk_cli.projects.report.features.features import \ - get_default_report_features -from multiversx_sdk_cli.projects.report.format.format_options import \ - FormatOptions +from multiversx_sdk_cli.projects.report.data.report import Report, merge_list_of_reports +from multiversx_sdk_cli.projects.report.features.features import ( + get_default_report_features, +) +from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions from multiversx_sdk_cli.projects.report.report_creator import ReportCreator logger = logging.getLogger("report") @@ -32,7 +31,13 @@ def _build_report(args: Any, build_options: Dict[str, Any]) -> None: args_copy = _prepare_args_for_build(args) build_args = cli_shared.convert_args_object_to_args_list(args_copy) - report_creator = ReportCreator(options, skip_build=args.skip_build, skip_twiggy=args.skip_twiggy, build_options=build_options, build_args=build_args) + report_creator = ReportCreator( + options, + skip_build=args.skip_build, + skip_twiggy=args.skip_twiggy, + build_options=build_options, + build_args=build_args, + ) report = report_creator.create_report(base_path, project_paths) _finalize_report(report, args) @@ -60,13 +65,13 @@ def _finalize_report(report: Report, args: Any) -> None: def _get_report_output_string(report: Report, args: Any) -> str: output_format = args.output_format - if output_format == 'github-markdown': + if output_format == "github-markdown": return report.to_markdown(FormatOptions(github_markdown=True)) - if output_format == 'text-markdown': + if output_format == "text-markdown": return report.to_markdown(FormatOptions(github_markdown=False)) - elif output_format == 'json': + elif output_format == "json": return report.to_json_string() - raise Exception('Invalid output format') + raise Exception("Invalid output format") def _store_output(output: str, args: Any) -> None: diff --git a/multiversx_sdk_cli/projects/report/features/features.py b/multiversx_sdk_cli/projects/report/features/features.py index 4ea081db..223115db 100644 --- a/multiversx_sdk_cli/projects/report/features/features.py +++ b/multiversx_sdk_cli/projects/report/features/features.py @@ -2,7 +2,9 @@ from multiversx_sdk_cli.projects.report.features.report_option import ReportFeature from multiversx_sdk_cli.projects.report.features.size import Size -from multiversx_sdk_cli.projects.report.features.twiggy_paths_check import TwiggyPathsCheck +from multiversx_sdk_cli.projects.report.features.twiggy_paths_check import ( + TwiggyPathsCheck, +) def get_default_report_features() -> List[ReportFeature]: diff --git a/multiversx_sdk_cli/projects/report/features/report_option.py b/multiversx_sdk_cli/projects/report/features/report_option.py index 67f87cbe..55f5ac21 100644 --- a/multiversx_sdk_cli/projects/report/features/report_option.py +++ b/multiversx_sdk_cli/projects/report/features/report_option.py @@ -23,7 +23,7 @@ def requires_twiggy_paths(self) -> bool: return False -def str_or_default(field: Optional[Any], default: str = '-') -> str: +def str_or_default(field: Optional[Any], default: str = "-") -> str: if field is None: return default return str(field) diff --git a/multiversx_sdk_cli/projects/report/features/twiggy_paths_check.py b/multiversx_sdk_cli/projects/report/features/twiggy_paths_check.py index 31fa51ff..22334727 100644 --- a/multiversx_sdk_cli/projects/report/features/twiggy_paths_check.py +++ b/multiversx_sdk_cli/projects/report/features/twiggy_paths_check.py @@ -3,8 +3,7 @@ from multiversx_sdk_cli import dependencies, myprocess, utils from multiversx_sdk_cli.errors import BadFile -from multiversx_sdk_cli.projects.report.features.report_option import \ - ReportFeature +from multiversx_sdk_cli.projects.report.features.report_option import ReportFeature logger = logging.getLogger("projects.report.options.twiggy_paths_check") @@ -21,7 +20,7 @@ def extract(self, wasm_path: Path) -> str: text = utils.read_text_file(twiggy_paths_path) return str(self.pattern in text) except BadFile: - return 'N/A' + return "N/A" def requires_twiggy_paths(self): return True @@ -32,7 +31,12 @@ def run_twiggy_paths(wasm_path: Path) -> Path: debug_wasm_path = _get_debug_wasm_path(wasm_path) twiggy_paths_args = ["twiggy", "paths", str(debug_wasm_path)] - output = myprocess.run_process(twiggy_paths_args, env=rust.get_env(), cwd=debug_wasm_path.parent, dump_to_stdout=False) + output = myprocess.run_process( + twiggy_paths_args, + env=rust.get_env(), + cwd=debug_wasm_path.parent, + dump_to_stdout=False, + ) output_path = _get_twiggy_paths_path(wasm_path) utils.write_file(output_path, output) @@ -52,16 +56,16 @@ def _add_file_prefix(file_path: Path, prefix: str) -> Path: def _get_debug_wasm_path(wasm_path: Path) -> Path: """ ->>> _get_debug_wasm_path(Path('test/contract.wasm')) -PosixPath('test/contract-dbg.wasm') + >>> _get_debug_wasm_path(Path('test/contract.wasm')) + PosixPath('test/contract-dbg.wasm') """ - return _replace_file_suffix(wasm_path, '-dbg.wasm') + return _replace_file_suffix(wasm_path, "-dbg.wasm") def _get_twiggy_paths_path(wasm_path: Path) -> Path: """ ->>> _get_twiggy_paths_path(Path('test/contract.wasm')) -PosixPath('test/twiggy-paths-contract-dbg.txt') + >>> _get_twiggy_paths_path(Path('test/contract.wasm')) + PosixPath('test/twiggy-paths-contract-dbg.txt') """ - txt_file_path = _replace_file_suffix(wasm_path, '-dbg.txt') - return _add_file_prefix(txt_file_path, 'twiggy-paths-') + txt_file_path = _replace_file_suffix(wasm_path, "-dbg.txt") + return _add_file_prefix(txt_file_path, "twiggy-paths-") diff --git a/multiversx_sdk_cli/projects/report/format/change_type.py b/multiversx_sdk_cli/projects/report/format/change_type.py index 7685ee82..5b036f23 100644 --- a/multiversx_sdk_cli/projects/report/format/change_type.py +++ b/multiversx_sdk_cli/projects/report/format/change_type.py @@ -18,20 +18,20 @@ def to_markdown(self, format_options: FormatOptions) -> str: def _to_github_markdown(self) -> str: switch = { - ChangeType.UNKNOWN: ':warning:', - ChangeType.NONE: '', - ChangeType.GOOD: ':green_circle:', - ChangeType.BAD: ':red_circle:', - ChangeType.MIXED: ':yellow_circle:' + ChangeType.UNKNOWN: ":warning:", + ChangeType.NONE: "", + ChangeType.GOOD: ":green_circle:", + ChangeType.BAD: ":red_circle:", + ChangeType.MIXED: ":yellow_circle:", } return switch[self] def _to_text_markdown(self) -> str: switch = { - ChangeType.UNKNOWN: '\u26a0\ufe0f ', - ChangeType.NONE: '', - ChangeType.GOOD: '\U0001F34F', - ChangeType.BAD: '\u274C', - ChangeType.MIXED: '\U0001F536\uFE0F' + ChangeType.UNKNOWN: "\u26a0\ufe0f ", + ChangeType.NONE: "", + ChangeType.GOOD: "\U0001f34f", + ChangeType.BAD: "\u274c", + ChangeType.MIXED: "\U0001f536\ufe0f", } return switch[self] diff --git a/multiversx_sdk_cli/projects/report/report_creator.py b/multiversx_sdk_cli/projects/report/report_creator.py index 11c30c87..9b67a7bd 100644 --- a/multiversx_sdk_cli/projects/report/report_creator.py +++ b/multiversx_sdk_cli/projects/report/report_creator.py @@ -1,4 +1,3 @@ - import itertools import operator from pathlib import Path @@ -8,25 +7,32 @@ from multiversx_sdk_cli.projects.core import load_project from multiversx_sdk_cli.projects.project_base import remove_suffix from multiversx_sdk_cli.projects.project_rust import ProjectRust -from multiversx_sdk_cli.projects.report.data.extracted_feature import \ - ExtractedFeature +from multiversx_sdk_cli.projects.report.data.extracted_feature import ExtractedFeature from multiversx_sdk_cli.projects.report.data.folder_report import FolderReport -from multiversx_sdk_cli.projects.report.data.project_report import \ - ProjectReport +from multiversx_sdk_cli.projects.report.data.project_report import ProjectReport from multiversx_sdk_cli.projects.report.data.report import Report from multiversx_sdk_cli.projects.report.data.wasm_report import WasmReport -from multiversx_sdk_cli.projects.report.features.report_option import \ - ReportFeature -from multiversx_sdk_cli.projects.report.features.twiggy_paths_check import \ - run_twiggy_paths +from multiversx_sdk_cli.projects.report.features.report_option import ReportFeature +from multiversx_sdk_cli.projects.report.features.twiggy_paths_check import ( + run_twiggy_paths, +) class ReportCreator: - def __init__(self, options: List[ReportFeature], skip_build: bool, skip_twiggy: bool, build_options: Dict[str, Any], build_args: List[str]) -> None: + def __init__( + self, + options: List[ReportFeature], + skip_build: bool, + skip_twiggy: bool, + build_options: Dict[str, Any], + build_args: List[str], + ) -> None: self.report_features = options self.skip_build = skip_build self.skip_twiggy = skip_twiggy - self.require_twiggy_paths = any(report_feature.requires_twiggy_paths() for report_feature in self.report_features) + self.require_twiggy_paths = any( + report_feature.requires_twiggy_paths() for report_feature in self.report_features + ) self.build_options = build_options self.build_args = build_args @@ -34,13 +40,17 @@ def create_report(self, base_path: Path, project_paths: List[Path]) -> Report: base_path = base_path.resolve() guards.is_directory(base_path) - folder_groups = [self._create_folder_report(base_path, parent_folder, iter) - for parent_folder, iter in _group_projects_by_folder(project_paths)] + folder_groups = [ + self._create_folder_report(base_path, parent_folder, iter) + for parent_folder, iter in _group_projects_by_folder(project_paths) + ] feature_names = [report_feature.name for report_feature in self.report_features] return Report(feature_names, folder_groups) - def _create_folder_report(self, base_path: Path, parent_folder: Path, iter: Iterable[Tuple[Path, Path]]) -> FolderReport: + def _create_folder_report( + self, base_path: Path, parent_folder: Path, iter: Iterable[Tuple[Path, Path]] + ) -> FolderReport: parent_folder = parent_folder.resolve() project_reports = [self._create_project_report(parent_folder, project_path) for _, project_path in iter] @@ -60,8 +70,10 @@ def _create_project_report(self, parent_path: Path, project_path: Path) -> Proje project.build_wasm_with_debug_symbols(self.build_options) twiggy_requirements_met = True - wasm_reports = [self._create_wasm_report(wasm_path, twiggy_requirements_met) for wasm_path in project.find_wasm_files()] - wasm_reports.sort(key=lambda report: remove_suffix(report.wasm_name, '.wasm')) + wasm_reports = [ + self._create_wasm_report(wasm_path, twiggy_requirements_met) for wasm_path in project.find_wasm_files() + ] + wasm_reports.sort(key=lambda report: remove_suffix(report.wasm_name, ".wasm")) return ProjectReport(project_path.relative_to(parent_path), wasm_reports) @@ -78,6 +90,8 @@ def _extract_feature(feature: ReportFeature, wasm_path: Path) -> ExtractedFeatur return ExtractedFeature(feature.name, [result]) -def _group_projects_by_folder(project_paths: List[Path]) -> Iterable[Tuple[Path, Iterable[Tuple[Path, Path]]]]: +def _group_projects_by_folder( + project_paths: List[Path], +) -> Iterable[Tuple[Path, Iterable[Tuple[Path, Path]]]]: path_pairs = sorted([(path.parent, path) for path in project_paths]) return itertools.groupby(path_pairs, operator.itemgetter(0)) diff --git a/multiversx_sdk_cli/projects/templates.py b/multiversx_sdk_cli/projects/templates.py index 3b5890b9..ae487303 100644 --- a/multiversx_sdk_cli/projects/templates.py +++ b/multiversx_sdk_cli/projects/templates.py @@ -9,12 +9,13 @@ class Contract: - def __init__(self, - tag: Union[str, None] = None, - name: Union[str, None] = None, - template: str = "", - path: Path = Path() - ) -> None: + def __init__( + self, + tag: Union[str, None] = None, + name: Union[str, None] = None, + template: str = "", + path: Path = Path(), + ) -> None: self.tag = tag self.name = name self.template = template diff --git a/multiversx_sdk_cli/sign_verify.py b/multiversx_sdk_cli/sign_verify.py index 77e4c77e..27903f50 100644 --- a/multiversx_sdk_cli/sign_verify.py +++ b/multiversx_sdk_cli/sign_verify.py @@ -24,14 +24,17 @@ def verify_signature(self) -> bool: message_computer = MessageComputer() verifier = UserVerifier.from_address(Address.new_from_bech32(self.address)) - is_signed = verifier.verify(message_computer.compute_bytes_for_signing(verifiable_message), verifiable_message.signature) + is_signed = verifier.verify( + message_computer.compute_bytes_for_signing(verifiable_message), + verifiable_message.signature, + ) return is_signed def to_dictionary(self) -> Dict[str, str]: return { "address": self.address, "message": self.message, - "signature": "0x" + self.signature + "signature": "0x" + self.signature, } diff --git a/multiversx_sdk_cli/simulation.py b/multiversx_sdk_cli/simulation.py index 4550f9e1..d2984b2c 100644 --- a/multiversx_sdk_cli/simulation.py +++ b/multiversx_sdk_cli/simulation.py @@ -1,13 +1,16 @@ from collections import OrderedDict from typing import Any, Dict, Protocol -from multiversx_sdk_cli.utils import ISerializable from multiversx_sdk import Transaction, TransactionOnNetwork +from multiversx_sdk_cli.utils import ISerializable + +# fmt: off class INetworkProvider(Protocol): def simulate_transaction(self, transaction: Transaction) -> TransactionOnNetwork: ... +# fmt: on class Simulation(ISerializable): @@ -21,7 +24,7 @@ def to_dictionary(self) -> Dict[str, Any]: return dictionary -class Simulator(): +class Simulator: def __init__(self, proxy: INetworkProvider) -> None: self.proxy = proxy diff --git a/multiversx_sdk_cli/tests/conftest.py b/multiversx_sdk_cli/tests/conftest.py index abff81f5..4a606102 100644 --- a/multiversx_sdk_cli/tests/conftest.py +++ b/multiversx_sdk_cli/tests/conftest.py @@ -3,7 +3,7 @@ # function executed right after test items collected but before test run def pytest_collection_modifyitems(config, items): - if not config.getoption('-m'): + if not config.getoption("-m"): skip_me = pytest.mark.skip(reason="require_localnet will only run if explicitly set to with -m") for item in items: if "require_localnet" in item.keywords: diff --git a/multiversx_sdk_cli/tests/local_verify_server.py b/multiversx_sdk_cli/tests/local_verify_server.py index dbc989ab..aa9cb1a3 100644 --- a/multiversx_sdk_cli/tests/local_verify_server.py +++ b/multiversx_sdk_cli/tests/local_verify_server.py @@ -1,7 +1,7 @@ import json from http.server import BaseHTTPRequestHandler, HTTPServer -HOST = 'localhost' +HOST = "localhost" PORT = 7777 @@ -12,18 +12,18 @@ def do_POST(self): self.end_headers() if self.path == "/initialise": - response = {'token': 7890} - self.wfile.write(bytes(json.dumps(response), 'utf-8')) + response = {"token": 7890} + self.wfile.write(bytes(json.dumps(response), "utf-8")) if self.path == "/verify": - response = {'status': 'sent to verification'} - self.wfile.write(bytes(json.dumps(response), 'utf-8')) + response = {"status": "sent to verification"} + self.wfile.write(bytes(json.dumps(response), "utf-8")) server = HTTPServer((HOST, PORT), HTTP) -print('Server running...') +print("Server running...") server.serve_forever() server.server_close() -print('Server closed!') +print("Server closed!") diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index 12e4c523..887d32b6 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -10,29 +10,33 @@ def test_contract_new(): - main([ - "contract", - "new", - "--template", - "adder", - "--path", - f"{parent}/testdata-out/SANDBOX" - ]) + main( + [ + "contract", + "new", + "--template", + "adder", + "--path", + f"{parent}/testdata-out/SANDBOX", + ] + ) assert Path.is_dir(parent / "testdata-out" / "SANDBOX" / "adder") def test_contract_new_with_bad_code(): # we change the contract code so the build would fail so we can catch the error - main([ - "contract", - "new", - "--template", - "adder", - "--path", - f"{parent}/testdata-out/SANDBOX", - "--name", - "adder-bad-src" - ]) + main( + [ + "contract", + "new", + "--template", + "adder", + "--path", + f"{parent}/testdata-out/SANDBOX", + "--name", + "adder-bad-src", + ] + ) assert Path.is_dir(parent / "testdata-out" / "SANDBOX" / "adder-bad-src") replace_variable_with_unknown_variable_for_adder() @@ -40,7 +44,10 @@ def test_contract_new_with_bad_code(): def replace_variable_with_unknown_variable_for_adder(): # this is done in order to replace the value added in the adder contract with a unknown variable - with open(parent / "testdata-out" / "SANDBOX" / "adder-bad-src" / "src" / "adder_bad_src.rs", "r") as f: + with open( + parent / "testdata-out" / "SANDBOX" / "adder-bad-src" / "src" / "adder_bad_src.rs", + "r", + ) as f: contract_lines = f.readlines() for index, line in reversed(list(enumerate(contract_lines))): @@ -48,18 +55,16 @@ def replace_variable_with_unknown_variable_for_adder(): contract_lines[index] = line.replace("value", "unknown_variable") break - with open(parent / "testdata-out" / "SANDBOX" / "adder-bad-src" / "src" / "adder_bad_src.rs", "w") as f: + with open( + parent / "testdata-out" / "SANDBOX" / "adder-bad-src" / "src" / "adder_bad_src.rs", + "w", + ) as f: f.writelines(contract_lines) @pytest.mark.skip_on_windows def test_contract_build(): - main([ - "contract", - "build", - "--path", - f"{parent}/testdata-out/SANDBOX/adder" - ]) + main(["contract", "build", "--path", f"{parent}/testdata-out/SANDBOX/adder"]) assert Path.is_file(parent / "testdata-out" / "SANDBOX" / "adder" / "output" / "adder.wasm") @@ -68,12 +73,7 @@ def test_contract_build(): def test_bad_contract_build(capsys: Any): ERROR = "Build error: error code = 101, see output." - main([ - "contract", - "build", - "--path", - f"{parent}/testdata-out/SANDBOX/adder-bad-src" - ]) + main(["contract", "build", "--path", f"{parent}/testdata-out/SANDBOX/adder-bad-src"]) out, _ = capsys.readouterr() @@ -173,60 +173,107 @@ def test_contract_transfer_and_execute(capsys: Any): first_token = "NFT-123456-02" second_token = "ESDT-987654" - main([ - "contract", "call", contract_address, - "--pem", f"{parent}/testdata/testUser.pem", - "--chain", "D", - "--nonce", "7", - "--gas-limit", "5000000", - "--function", "add", - "--arguments", "5", - "--token-transfers", first_token, "1" - ]) + main( + [ + "contract", + "call", + contract_address, + "--pem", + f"{parent}/testdata/testUser.pem", + "--chain", + "D", + "--nonce", + "7", + "--gas-limit", + "5000000", + "--function", + "add", + "--arguments", + "5", + "--token-transfers", + first_token, + "1", + ] + ) data = get_transaction_data(capsys) - assert data == "ESDTNFTTransfer@4e46542d313233343536@02@01@0000000000000000050067a1fd3a146a1d9a6df532d0f6b004e2b29153ba69e1@616464@05" + assert ( + data + == "ESDTNFTTransfer@4e46542d313233343536@02@01@0000000000000000050067a1fd3a146a1d9a6df532d0f6b004e2b29153ba69e1@616464@05" + ) # Clear the captured content capsys.readouterr() - main([ - "contract", "call", contract_address, - "--pem", f"{parent}/testdata/testUser.pem", - "--chain", "D", - "--nonce", "77", - "--gas-limit", "5000000", - "--function", "add", - "--arguments", "5", - "--token-transfers", first_token, "1", second_token, "100" - ]) + main( + [ + "contract", + "call", + contract_address, + "--pem", + f"{parent}/testdata/testUser.pem", + "--chain", + "D", + "--nonce", + "77", + "--gas-limit", + "5000000", + "--function", + "add", + "--arguments", + "5", + "--token-transfers", + first_token, + "1", + second_token, + "100", + ] + ) data = get_transaction_data(capsys) - assert data == "MultiESDTNFTTransfer@0000000000000000050067a1fd3a146a1d9a6df532d0f6b004e2b29153ba69e1@02@4e46542d313233343536@02@01@455344542d393837363534@@64@616464@05" + assert ( + data + == "MultiESDTNFTTransfer@0000000000000000050067a1fd3a146a1d9a6df532d0f6b004e2b29153ba69e1@02@4e46542d313233343536@02@01@455344542d393837363534@@64@616464@05" + ) def test_contract_flow(capsys: Any): alice = f"{parent}/testdata/alice.pem" adder = f"{parent}/testdata/adder.wasm" - main([ - "contract", "deploy", - "--bytecode", adder, - "--pem", alice, - "--recall-nonce", - "--gas-limit", "5000000", - "--proxy", "https://testnet-api.multiversx.com", - "--arguments", "0", - "--send", "--wait-result" - ]) + main( + [ + "contract", + "deploy", + "--bytecode", + adder, + "--pem", + alice, + "--recall-nonce", + "--gas-limit", + "5000000", + "--proxy", + "https://testnet-api.multiversx.com", + "--arguments", + "0", + "--send", + "--wait-result", + ] + ) contract = get_contract_address(capsys) # Clear the captured content capsys.readouterr() - main([ - "contract", "query", contract, - "--function", "getSum", - "--proxy", "https://testnet-api.multiversx.com" - ]) + main( + [ + "contract", + "query", + contract, + "--function", + "getSum", + "--proxy", + "https://testnet-api.multiversx.com", + ] + ) response = get_query_response(capsys) assert len(response) == 1 assert response == [""] @@ -234,25 +281,41 @@ def test_contract_flow(capsys: Any): # Clear the captured content capsys.readouterr() - main([ - "contract", "call", contract, - "--pem", alice, - "--function", "add", - "--recall-nonce", - "--gas-limit", "5000000", - "--proxy", "https://testnet-api.multiversx.com", - "--arguments", "7", - "--send", "--wait-result" - ]) + main( + [ + "contract", + "call", + contract, + "--pem", + alice, + "--function", + "add", + "--recall-nonce", + "--gas-limit", + "5000000", + "--proxy", + "https://testnet-api.multiversx.com", + "--arguments", + "7", + "--send", + "--wait-result", + ] + ) # Clear the captured content capsys.readouterr() - main([ - "contract", "query", contract, - "--function", "getSum", - "--proxy", "https://testnet-api.multiversx.com" - ]) + main( + [ + "contract", + "query", + contract, + "--function", + "getSum", + "--proxy", + "https://testnet-api.multiversx.com", + ] + ) response = get_query_response(capsys) assert len(response) == 1 assert response == ["07"] @@ -260,31 +323,49 @@ def test_contract_flow(capsys: Any): # Clear the captured content capsys.readouterr() - main([ - "contract", "upgrade", contract, - "--bytecode", adder, - "--pem", alice, - "--recall-nonce", - "--gas-limit", "5000000", - "--proxy", "https://testnet-api.multiversx.com", - "--arguments", "0", - "--send", "--wait-result" - ]) + main( + [ + "contract", + "upgrade", + contract, + "--bytecode", + adder, + "--pem", + alice, + "--recall-nonce", + "--gas-limit", + "5000000", + "--proxy", + "https://testnet-api.multiversx.com", + "--arguments", + "0", + "--send", + "--wait-result", + ] + ) def test_contract_deploy_without_required_arguments(): alice = f"{parent}/testdata/alice.pem" adder = f"{parent}/testdata/adder.wasm" - return_code = main([ - "contract", "deploy", - "--bytecode", adder, - "--pem", alice, - "--recall-nonce", - "--gas-limit", "5000000", - "--arguments", "0", - "--send", "--wait-result" - ]) + return_code = main( + [ + "contract", + "deploy", + "--bytecode", + adder, + "--pem", + alice, + "--recall-nonce", + "--gas-limit", + "5000000", + "--arguments", + "0", + "--send", + "--wait-result", + ] + ) assert return_code @@ -292,26 +373,44 @@ def test_contract_commands_argument_parameter(): alice = f"{parent}/testdata/alice.pem" adder = f"{parent}/testdata/adder.wasm" - return_code = main([ - "contract", "deploy", - "--bytecode", adder, - "--pem", alice, - "--nonce", "7", - "--chain", "D", - "--gas-limit", "5000000", - "--arguments", "foobar", - ]) + return_code = main( + [ + "contract", + "deploy", + "--bytecode", + adder, + "--pem", + alice, + "--nonce", + "7", + "--chain", + "D", + "--gas-limit", + "5000000", + "--arguments", + "foobar", + ] + ) assert return_code - return_code = main([ - "contract", "deploy", - "--bytecode", adder, - "--pem", alice, - "--nonce", "7", - "--chain", "D", - "--gas-limit", "5000000", - "--arguments", "str:foobar", - ]) + return_code = main( + [ + "contract", + "deploy", + "--bytecode", + adder, + "--pem", + alice, + "--nonce", + "7", + "--chain", + "D", + "--gas-limit", + "5000000", + "--arguments", + "str:foobar", + ] + ) assert not return_code @@ -320,77 +419,130 @@ def test_contract_deploy_with_abi(capsys: Any): multisig = f"{parent}/testdata/multisig.wasm" multisig_abi = f"{parent}/testdata/multisig.abi.json" - return_code = main([ - "contract", "deploy", - "--bytecode", multisig, - "--pem", alice, - "--chain", "T", - "--nonce", "7", - "--gas-limit", "5000000", - "--arguments", "2", "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" - ]) + return_code = main( + [ + "contract", + "deploy", + "--bytecode", + multisig, + "--pem", + alice, + "--chain", + "T", + "--nonce", + "7", + "--gas-limit", + "5000000", + "--arguments", + "2", + "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + ] + ) assert not return_code deploy_without_abi_data = get_transaction_data(capsys) # Clear the captured content capsys.readouterr() - return_code = main([ - "contract", "deploy", - "--bytecode", multisig, - "--abi", multisig_abi, - "--pem", alice, - "--chain", "T", - "--nonce", "7", - "--gas-limit", "5000000", - "--arguments-file", f"{parent}/testdata/deploy_multisig_args.json" - ]) + return_code = main( + [ + "contract", + "deploy", + "--bytecode", + multisig, + "--abi", + multisig_abi, + "--pem", + alice, + "--chain", + "T", + "--nonce", + "7", + "--gas-limit", + "5000000", + "--arguments-file", + f"{parent}/testdata/deploy_multisig_args.json", + ] + ) assert not return_code deploy_with_abi_data = get_transaction_data(capsys) assert deploy_without_abi_data == deploy_with_abi_data - assert deploy_without_abi_data.endswith("@02@0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1@c0006edaaee4fd479f2f248b341eb11eaecaec4d7dee190619958332bba5200f") - assert deploy_with_abi_data.endswith("@02@0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1@c0006edaaee4fd479f2f248b341eb11eaecaec4d7dee190619958332bba5200f") + assert deploy_without_abi_data.endswith( + "@02@0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1@c0006edaaee4fd479f2f248b341eb11eaecaec4d7dee190619958332bba5200f" + ) + assert deploy_with_abi_data.endswith( + "@02@0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1@c0006edaaee4fd479f2f248b341eb11eaecaec4d7dee190619958332bba5200f" + ) def test_contract_call_with_abi(capsys: Any): alice = f"{parent}/testdata/alice.pem" multisig_abi = f"{parent}/testdata/multisig.abi.json" - return_code = main([ - "contract", "call", "erd1qqqqqqqqqqqqqpgqpg2tnjelx3s3dhkksqhjavprtgmt9xt4d8ss9al8m4", - "--pem", alice, - "--chain", "T", - "--nonce", "7", - "--gas-limit", "5000000", - "--function", "proposeBatch", - "--abi", multisig_abi, - "--arguments-file", f"{parent}/testdata/call_multisig_propose_batch_args.json" - ]) + return_code = main( + [ + "contract", + "call", + "erd1qqqqqqqqqqqqqpgqpg2tnjelx3s3dhkksqhjavprtgmt9xt4d8ss9al8m4", + "--pem", + alice, + "--chain", + "T", + "--nonce", + "7", + "--gas-limit", + "5000000", + "--function", + "proposeBatch", + "--abi", + multisig_abi, + "--arguments-file", + f"{parent}/testdata/call_multisig_propose_batch_args.json", + ] + ) assert not return_code data = get_transaction_data(capsys) - assert data == "proposeBatch@0500000000000000000500ed8e25a94efa837aae0e593112cfbb01b448755069e1000000080de0b6b3a7640000010000000000e4e1c000000003616464000000010000000107" + assert ( + data + == "proposeBatch@0500000000000000000500ed8e25a94efa837aae0e593112cfbb01b448755069e1000000080de0b6b3a7640000010000000000e4e1c000000003616464000000010000000107" + ) def test_contract_upgrade_with_abi(capsys: Any): alice = f"{parent}/testdata/alice.pem" multisig_abi = f"{parent}/testdata/multisig.abi.json" - return_code = main([ - "contract", "call", "erd1qqqqqqqqqqqqqpgqpg2tnjelx3s3dhkksqhjavprtgmt9xt4d8ss9al8m4", - "--pem", alice, - "--chain", "T", - "--nonce", "7", - "--gas-limit", "5000000", - "--function", "proposeSCUpgradeFromSource", - "--abi", multisig_abi, - "--arguments-file", f"{parent}/testdata/upgrade_multisig_args.json" - ]) + return_code = main( + [ + "contract", + "call", + "erd1qqqqqqqqqqqqqpgqpg2tnjelx3s3dhkksqhjavprtgmt9xt4d8ss9al8m4", + "--pem", + alice, + "--chain", + "T", + "--nonce", + "7", + "--gas-limit", + "5000000", + "--function", + "proposeSCUpgradeFromSource", + "--abi", + multisig_abi, + "--arguments-file", + f"{parent}/testdata/upgrade_multisig_args.json", + ] + ) assert not return_code data = get_transaction_data(capsys) - assert data == "proposeSCUpgradeFromSource@000000000000000005000a14b9cb3f346116ded6802f2eb0235a36b2997569e1@@00000000000000000500ed8e25a94efa837aae0e593112cfbb01b448755069e1@0500@" + assert ( + data + == "proposeSCUpgradeFromSource@000000000000000005000a14b9cb3f346116ded6802f2eb0235a36b2997569e1@@00000000000000000500ed8e25a94efa837aae0e593112cfbb01b448755069e1@0500@" + ) def test_contract_query(capsys: Any): @@ -398,44 +550,70 @@ def test_contract_query(capsys: Any): adder = f"{parent}/testdata/adder.wasm" adder_abi = f"{parent}/testdata/adder.abi.json" - return_code = main([ - "contract", "deploy", - "--bytecode", adder, - "--abi", adder_abi, - "--pem", alice, - "--recall-nonce", - "--gas-limit", "5000000", - "--proxy", "https://testnet-api.multiversx.com", - "--arguments", "0", - "--send", "--wait-result" - ]) + return_code = main( + [ + "contract", + "deploy", + "--bytecode", + adder, + "--abi", + adder_abi, + "--pem", + alice, + "--recall-nonce", + "--gas-limit", + "5000000", + "--proxy", + "https://testnet-api.multiversx.com", + "--arguments", + "0", + "--send", + "--wait-result", + ] + ) assert not return_code contract = get_contract_address(capsys) # Clear the captured content capsys.readouterr() - return_code = main([ - "contract", "call", contract, - "--pem", alice, - "--function", "add", - "--recall-nonce", - "--gas-limit", "5000000", - "--proxy", "https://testnet-api.multiversx.com", - "--arguments", "14", - "--send", "--wait-result" - ]) + return_code = main( + [ + "contract", + "call", + contract, + "--pem", + alice, + "--function", + "add", + "--recall-nonce", + "--gas-limit", + "5000000", + "--proxy", + "https://testnet-api.multiversx.com", + "--arguments", + "14", + "--send", + "--wait-result", + ] + ) assert not return_code # Clear the captured content capsys.readouterr() # invalid, without abi - return_code = main([ - "contract", "query", contract, - "--function", "getSummm", - "--proxy", "https://testnet-api.multiversx.com" - ]) + return_code = main( + [ + "contract", + "query", + contract, + "--function", + "getSummm", + "--proxy", + "https://testnet-api.multiversx.com", + ] + ) assert return_code output = _read_stdout(capsys) if "invalid function (not found)" in output: @@ -447,12 +625,19 @@ def test_contract_query(capsys: Any): capsys.readouterr() # invalid, with abi, error is thrown by sdk-py - return_code = main([ - "contract", "query", contract, - "--function", "getSummm", - "--abi", adder_abi, - "--proxy", "https://testnet-api.multiversx.com" - ]) + return_code = main( + [ + "contract", + "query", + contract, + "--function", + "getSummm", + "--abi", + adder_abi, + "--proxy", + "https://testnet-api.multiversx.com", + ] + ) assert return_code output = _read_stdout(capsys) if "endpoint 'getSummm' not found" in output: @@ -464,11 +649,17 @@ def test_contract_query(capsys: Any): capsys.readouterr() # query contract, without abi - return_code = main([ - "contract", "query", contract, - "--function", "getSum", - "--proxy", "https://testnet-api.multiversx.com" - ]) + return_code = main( + [ + "contract", + "query", + contract, + "--function", + "getSum", + "--proxy", + "https://testnet-api.multiversx.com", + ] + ) assert not return_code response = get_query_response(capsys) assert response == ["0e"] @@ -477,12 +668,19 @@ def test_contract_query(capsys: Any): capsys.readouterr() # query contract, without abi - return_code = main([ - "contract", "query", contract, - "--function", "getSum", - "--abi", adder_abi, - "--proxy", "https://testnet-api.multiversx.com" - ]) + return_code = main( + [ + "contract", + "query", + contract, + "--function", + "getSum", + "--abi", + adder_abi, + "--proxy", + "https://testnet-api.multiversx.com", + ] + ) assert not return_code response = get_query_response(capsys) assert response == [14] diff --git a/multiversx_sdk_cli/tests/test_cli_dns.py b/multiversx_sdk_cli/tests/test_cli_dns.py index 3d2265a9..e038bb31 100644 --- a/multiversx_sdk_cli/tests/test_cli_dns.py +++ b/multiversx_sdk_cli/tests/test_cli_dns.py @@ -8,14 +8,22 @@ def test_prepare_relayed_dns_register_transaction(): alice = testdata_path / "alice.pem" - return_code = main([ - "dns", "register", - "--pem", str(alice), - "--name", "alice.elrond", - "--nonce", "0", - "--gas-limit", "15000000", - "--chain", "T", - "--relay" - ]) + return_code = main( + [ + "dns", + "register", + "--pem", + str(alice), + "--name", + "alice.elrond", + "--nonce", + "0", + "--gas-limit", + "15000000", + "--chain", + "T", + "--relay", + ] + ) assert False if return_code else True diff --git a/multiversx_sdk_cli/tests/test_cli_staking_provider.py b/multiversx_sdk_cli/tests/test_cli_staking_provider.py index a657c0d1..82e21c2b 100644 --- a/multiversx_sdk_cli/tests/test_cli_staking_provider.py +++ b/multiversx_sdk_cli/tests/test_cli_staking_provider.py @@ -13,15 +13,25 @@ def test_create_new_delegation_contract(capsys: Any): - main([ - "staking-provider", "create-new-delegation-contract", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--value", "1250000000000000000000", - "--total-delegation-cap", "10000000000000000000000", - "--service-fee", "100", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "create-new-delegation-contract", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--value", + "1250000000000000000000", + "--total-delegation-cap", + "10000000000000000000000", + "--service-fee", + "100", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -32,20 +42,34 @@ def test_create_new_delegation_contract(capsys: Any): assert transaction["chainID"] == "T" assert transaction["gasLimit"] == 60126500 assert transaction["value"] == "1250000000000000000000" - assert transaction["signature"] == "0a6d7249c671b1db00f1b8807770bb64eac51e2e2779e426f35439c6cb7b00dadd023392a061ba1b6ee35d235ac2c0ad87283413b1d5558d8526bc5712588702" + assert ( + transaction["signature"] + == "0a6d7249c671b1db00f1b8807770bb64eac51e2e2779e426f35439c6cb7b00dadd023392a061ba1b6ee35d235ac2c0ad87283413b1d5558d8526bc5712588702" + ) def test_create_new_delegation_contract_with_provided_gas_limit(capsys: Any): - main([ - "staking-provider", "create-new-delegation-contract", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--value", "1250000000000000000000", - "--total-delegation-cap", "10000000000000000000000", - "--service-fee", "100", - "--chain", "T", - "--gas-limit", "60126501" - ]) + main( + [ + "staking-provider", + "create-new-delegation-contract", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--value", + "1250000000000000000000", + "--total-delegation-cap", + "10000000000000000000000", + "--service-fee", + "100", + "--chain", + "T", + "--gas-limit", + "60126501", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -56,183 +80,301 @@ def test_create_new_delegation_contract_with_provided_gas_limit(capsys: Any): assert transaction["chainID"] == "T" assert transaction["gasLimit"] == 60126501 assert transaction["value"] == "1250000000000000000000" - assert transaction["signature"] == "8e28aa5a11454d975a4841086397000de053784b04e68402da9abcf26adc8726d3ce32f415119e3aa38746f2e5524063a6176a0c8cd88c9e8e89f9626d045202" + assert ( + transaction["signature"] + == "8e28aa5a11454d975a4841086397000de053784b04e68402da9abcf26adc8726d3ce32f415119e3aa38746f2e5524063a6176a0c8cd88c9e8e89f9626d045202" + ) def test_add_nodes(capsys: Any): validators_file = parent / "testdata" / "validators.json" - main([ - "staking-provider", "add-nodes", - "--validators-file", str(validators_file), - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", "--estimate-gas" - ]) + main( + [ + "staking-provider", + "add-nodes", + "--validators-file", + str(validators_file), + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--estimate-gas", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "addNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208@307ef00b648eed52ce4e95f8155f2e65491addd49266254f7c32ff06622e335752a6a6ad2efeb2534ec0db6a431b4989@78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d@4c90003d4b535fe709b6583708ae276e29558c58d160ad6241c3063e590611a2e327f2b8299b4955179a85eab50b4587@7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491@1d6cf6b0a38fa5c10df6493eae0b14c5d119d9f7d3d3e790a047e4e7c09919f64f910971ceef380b49f70fc982d07d19" + assert ( + data + == "addNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208@307ef00b648eed52ce4e95f8155f2e65491addd49266254f7c32ff06622e335752a6a6ad2efeb2534ec0db6a431b4989@78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d@4c90003d4b535fe709b6583708ae276e29558c58d160ad6241c3063e590611a2e327f2b8299b4955179a85eab50b4587@7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491@1d6cf6b0a38fa5c10df6493eae0b14c5d119d9f7d3d3e790a047e4e7c09919f64f910971ceef380b49f70fc982d07d19" + ) assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 20367000 - assert transaction["signature"] == "a0a8ce8ee6c60bd44b3940e47f3598fc95b47d111cf3ce7752951c692f98ccc8e0a2eade3ac55fcc68c54f98c69a0fb7aa0c93a50e8a8bcd0206f84a95e6390a" + assert ( + transaction["signature"] + == "a0a8ce8ee6c60bd44b3940e47f3598fc95b47d111cf3ce7752951c692f98ccc8e0a2eade3ac55fcc68c54f98c69a0fb7aa0c93a50e8a8bcd0206f84a95e6390a" + ) def test_add_nodes_with_gas_limit(capsys: Any): validators_file = parent / "testdata" / "validators.json" - main([ - "staking-provider", "add-nodes", - "--validators-file", str(validators_file), - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", - "--gas-limit", "20367001" - ]) + main( + [ + "staking-provider", + "add-nodes", + "--validators-file", + str(validators_file), + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--gas-limit", + "20367001", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "addNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208@307ef00b648eed52ce4e95f8155f2e65491addd49266254f7c32ff06622e335752a6a6ad2efeb2534ec0db6a431b4989@78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d@4c90003d4b535fe709b6583708ae276e29558c58d160ad6241c3063e590611a2e327f2b8299b4955179a85eab50b4587@7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491@1d6cf6b0a38fa5c10df6493eae0b14c5d119d9f7d3d3e790a047e4e7c09919f64f910971ceef380b49f70fc982d07d19" + assert ( + data + == "addNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208@307ef00b648eed52ce4e95f8155f2e65491addd49266254f7c32ff06622e335752a6a6ad2efeb2534ec0db6a431b4989@78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d@4c90003d4b535fe709b6583708ae276e29558c58d160ad6241c3063e590611a2e327f2b8299b4955179a85eab50b4587@7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491@1d6cf6b0a38fa5c10df6493eae0b14c5d119d9f7d3d3e790a047e4e7c09919f64f910971ceef380b49f70fc982d07d19" + ) assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 20367001 - assert transaction["signature"] == "b4aa91a3c865cf82784f29a659c0884cb70948e0e3f0b74ec8fe5a3cce358e750c357f1a1f9378e855a4261fff10abd927d1e3535fd18e99c40a562e31d67406" + assert ( + transaction["signature"] + == "b4aa91a3c865cf82784f29a659c0884cb70948e0e3f0b74ec8fe5a3cce358e750c357f1a1f9378e855a4261fff10abd927d1e3535fd18e99c40a562e31d67406" + ) def test_remove_nodes_with_bls_keys(capsys: Any): - main([ - "staking-provider", "remove-nodes", - "--bls-keys", f"{first_bls_key},{second_bls_key}", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", "--estimate-gas" - ]) + main( + [ + "staking-provider", + "remove-nodes", + "--bls-keys", + f"{first_bls_key},{second_bls_key}", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--estimate-gas", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "removeNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + assert ( + data + == "removeNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + ) assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 13645500 def test_remove_nodes_with_validators_file(capsys: Any): - main([ - "staking-provider", "remove-nodes", - "--validators-file", str(validators_file), - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", "--estimate-gas" - ]) + main( + [ + "staking-provider", + "remove-nodes", + "--validators-file", + str(validators_file), + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--estimate-gas", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "removeNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + assert ( + data + == "removeNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + ) assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 13645500 def test_stake_nodes_with_bls_keys(capsys: Any): - main([ - "staking-provider", "stake-nodes", - "--validators-file", str(validators_file), - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", "--estimate-gas" - ]) + main( + [ + "staking-provider", + "stake-nodes", + "--validators-file", + str(validators_file), + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--estimate-gas", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "stakeNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + assert ( + data + == "stakeNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + ) assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 18644000 def test_stake_nodes_with_validators_file(capsys: Any): - main([ - "staking-provider", "stake-nodes", - "--bls-keys", f"{first_bls_key},{second_bls_key}", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", "--estimate-gas" - ]) + main( + [ + "staking-provider", + "stake-nodes", + "--bls-keys", + f"{first_bls_key},{second_bls_key}", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--estimate-gas", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "stakeNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + assert ( + data + == "stakeNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + ) assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 18644000 def test_unbond_nodes(capsys: Any): - main([ - "staking-provider", "unbond-nodes", - "--bls-keys", f"{first_bls_key},{second_bls_key}", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", "--estimate-gas" - ]) + main( + [ + "staking-provider", + "unbond-nodes", + "--bls-keys", + f"{first_bls_key},{second_bls_key}", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--estimate-gas", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "unBondNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + assert ( + data + == "unBondNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + ) assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 18645500 def test_unstake_nodes(capsys: Any): - main([ - "staking-provider", "unstake-nodes", - "--bls-keys", f"{first_bls_key},{second_bls_key}", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", "--estimate-gas" - ]) + main( + [ + "staking-provider", + "unstake-nodes", + "--bls-keys", + f"{first_bls_key},{second_bls_key}", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--estimate-gas", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "unStakeNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + assert ( + data + == "unStakeNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + ) assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 18647000 def test_unjail_nodes(capsys: Any): - main([ - "staking-provider", "unjail-nodes", - "--bls-keys", f"{first_bls_key},{second_bls_key}", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--value", "5000000000000000000", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", "--estimate-gas" - ]) + main( + [ + "staking-provider", + "unjail-nodes", + "--bls-keys", + f"{first_bls_key},{second_bls_key}", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--value", + "5000000000000000000", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--estimate-gas", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "unJailNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + assert ( + data + == "unJailNodes@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" + ) assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 13645500 @@ -240,14 +382,23 @@ def test_unjail_nodes(capsys: Any): def test_change_service_fee(capsys: Any): - main([ - "staking-provider", "change-service-fee", - "--service-fee", "100", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", "--estimate-gas" - ]) + main( + [ + "staking-provider", + "change-service-fee", + "--service-fee", + "100", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--estimate-gas", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -259,14 +410,23 @@ def test_change_service_fee(capsys: Any): def test_modify_delegation_cap(capsys: Any): - main([ - "staking-provider", "modify-delegation-cap", - "--delegation-cap", "10000000000000000000000", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--chain", "T", - "--nonce", "7", "--estimate-gas" - ]) + main( + [ + "staking-provider", + "modify-delegation-cap", + "--delegation-cap", + "10000000000000000000000", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--chain", + "T", + "--nonce", + "7", + "--estimate-gas", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -278,14 +438,22 @@ def test_modify_delegation_cap(capsys: Any): def test_automatic_activation(capsys: Any): - main([ - "staking-provider", "automatic-activation", - "--set", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "automatic-activation", + "--set", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -298,14 +466,22 @@ def test_automatic_activation(capsys: Any): # Clear the captured content capsys.readouterr() - main([ - "staking-provider", "automatic-activation", - "--unset", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "automatic-activation", + "--unset", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -317,14 +493,22 @@ def test_automatic_activation(capsys: Any): def test_redelegate_cap(capsys: Any): - main([ - "staking-provider", "redelegate-cap", - "--set", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "redelegate-cap", + "--set", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -337,14 +521,22 @@ def test_redelegate_cap(capsys: Any): # Clear the captured content capsys.readouterr() - main([ - "staking-provider", "redelegate-cap", - "--unset", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "redelegate-cap", + "--unset", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -356,16 +548,27 @@ def test_redelegate_cap(capsys: Any): def test_set_metadata(capsys: Any): - main([ - "staking-provider", "set-metadata", - "--name", "Test", - "--website", "www.test.com", - "--identifier", "TEST", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "set-metadata", + "--name", + "Test", + "--website", + "www.test.com", + "--identifier", + "TEST", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -377,14 +580,23 @@ def test_set_metadata(capsys: Any): def test_create_delegation_contract_from_validator(capsys: Any): - main([ - "staking-provider", "make-delegation-contract-from-validator", - "--max-cap", "0", - "--fee", "3745", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "make-delegation-contract-from-validator", + "--max-cap", + "0", + "--fee", + "3745", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -396,14 +608,23 @@ def test_create_delegation_contract_from_validator(capsys: Any): def test_delegate(capsys: Any): - main([ - "staking-provider", "delegate", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--value", "1000000000000000000", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "delegate", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--value", + "1000000000000000000", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -415,13 +636,21 @@ def test_delegate(capsys: Any): def test_claim_rewards(capsys: Any): - main([ - "staking-provider", "claim-rewards", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "claim-rewards", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -433,13 +662,21 @@ def test_claim_rewards(capsys: Any): def test_redelegate_rewards(capsys: Any): - main([ - "staking-provider", "redelegate-rewards", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "redelegate-rewards", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -451,14 +688,23 @@ def test_redelegate_rewards(capsys: Any): def test_undelegate(capsys: Any): - main([ - "staking-provider", "undelegate", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--value", "1000000000000000000", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "undelegate", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--value", + "1000000000000000000", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] @@ -470,13 +716,21 @@ def test_undelegate(capsys: Any): def test_withdraw(capsys: Any): - main([ - "staking-provider", "withdraw", - "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", - "--pem", str(alice), - "--nonce", "7", "--estimate-gas", - "--chain", "T" - ]) + main( + [ + "staking-provider", + "withdraw", + "--delegation-contract", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", + "--pem", + str(alice), + "--nonce", + "7", + "--estimate-gas", + "--chain", + "T", + ] + ) tx = get_transaction(capsys) data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index b7bcd4a3..1224e955 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -12,110 +12,192 @@ def test_relayed_v1_transaction(capsys: Any): multi_user_pem = testdata_path / "multiple_addresses.pem" address_index = 1 - return_code = main([ - "tx", "new", - "--pem", str(multi_user_pem), - "--pem-index", str(address_index), - "--receiver", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", - "--nonce", "198", - "--gas-limit", "60000000", - "--data", "getContractConfig", - "--version", "1", - "--chain", "T", - "--relay" - ]) + return_code = main( + [ + "tx", + "new", + "--pem", + str(multi_user_pem), + "--pem-index", + str(address_index), + "--receiver", + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", + "--nonce", + "198", + "--gas-limit", + "60000000", + "--data", + "getContractConfig", + "--version", + "1", + "--chain", + "T", + "--relay", + ] + ) assert return_code == 0 relayed_tx = _read_stdout(capsys) - assert relayed_tx == "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414141415141414141414141414141414141414141414141414141414141432f2f383d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225a3256305132397564484a68593352446232356d6157633d222c227369676e6174757265223a2239682b6e6742584f5536776674315464437368534d4b3454446a5a32794f74686336564c576e3478724d5a706248427738677a6c6659596d362b766b505258303764634a562b4745635462616a7049692b5a5a5942773d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a317d" + assert ( + relayed_tx + == "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414141415141414141414141414141414141414141414141414141414141432f2f383d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225a3256305132397564484a68593352446232356d6157633d222c227369676e6174757265223a2239682b6e6742584f5536776674315464437368534d4b3454446a5a32794f74686336564c576e3478724d5a706248427738677a6c6659596d362b766b505258303764634a562b4745635462616a7049692b5a5a5942773d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a317d" + ) def test_create_tx_and_sign_by_hash(capsys: Any): - return_code = main([ - "tx", "new", - "--pem", str(testdata_path / "alice.pem"), - "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--nonce", "89", - "--gas-limit", "50000", - "--version", "2", - "--options", "1", - "--chain", "integration tests chain ID", - ]) + return_code = main( + [ + "tx", + "new", + "--pem", + str(testdata_path / "alice.pem"), + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", + "89", + "--gas-limit", + "50000", + "--version", + "2", + "--options", + "1", + "--chain", + "integration tests chain ID", + ] + ) assert return_code == 0 tx = _read_stdout(capsys) tx_json = json.loads(tx) signature = tx_json["emittedTransaction"]["signature"] - assert signature == "f0c81f2393b1ec5972c813f817bae8daa00ade91c6f75ea604ab6a4d2797aca4378d783023ff98f1a02717fe4f24240cdfba0b674ee9abb18042203d713bc70a" + assert ( + signature + == "f0c81f2393b1ec5972c813f817bae8daa00ade91c6f75ea604ab6a4d2797aca4378d783023ff98f1a02717fe4f24240cdfba0b674ee9abb18042203d713bc70a" + ) def test_create_move_balance_transaction(capsys: Any): - return_code = main([ - "tx", "new", - "--pem", str(testdata_path / "alice.pem"), - "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--nonce", "215", - "--gas-limit", "500000", - "--value", "1000000000000", - "--data", "hello", - "--version", "2", - "--options", "0", - "--chain", "T", - ]) + return_code = main( + [ + "tx", + "new", + "--pem", + str(testdata_path / "alice.pem"), + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", + "215", + "--gas-limit", + "500000", + "--value", + "1000000000000", + "--data", + "hello", + "--version", + "2", + "--options", + "0", + "--chain", + "T", + ] + ) assert return_code == 0 tx = _read_stdout(capsys) tx_json = json.loads(tx) signature = tx_json["emittedTransaction"]["signature"] - assert signature == "e88d846800bab1751e222c4461a310a3882312ef6d75fd8b861a2f3b572837b58f146ff9d60d16e617f53358d6cfa87cbcc65ad624c77003779d474059264901" + assert ( + signature + == "e88d846800bab1751e222c4461a310a3882312ef6d75fd8b861a2f3b572837b58f146ff9d60d16e617f53358d6cfa87cbcc65ad624c77003779d474059264901" + ) def test_create_multi_transfer_transaction(capsys: Any): - return_code = main([ - "tx", "new", - "--pem", str(testdata_path / "alice.pem"), - "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--nonce", "212", - "--gas-limit", "5000000", - "--token-transfers", "SSSSS-941b91-01", "1", "TEST-738c3d", "1200000000", - "--version", "2", - "--options", "0", - "--chain", "T", - ]) + return_code = main( + [ + "tx", + "new", + "--pem", + str(testdata_path / "alice.pem"), + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", + "212", + "--gas-limit", + "5000000", + "--token-transfers", + "SSSSS-941b91-01", + "1", + "TEST-738c3d", + "1200000000", + "--version", + "2", + "--options", + "0", + "--chain", + "T", + ] + ) assert return_code == 0 tx = _read_stdout(capsys) tx_json = json.loads(tx) signature = tx_json["emittedTransaction"]["signature"] - assert signature == "575b029d52ff5ffbfb7bab2f04052de88a6f7d022a6ad368459b8af9acaed3717d3f95db09f460649a8f405800838bc2c432496bd03c9039ea166bd32b84660e" + assert ( + signature + == "575b029d52ff5ffbfb7bab2f04052de88a6f7d022a6ad368459b8af9acaed3717d3f95db09f460649a8f405800838bc2c432496bd03c9039ea166bd32b84660e" + ) def test_create_multi_transfer_transaction_with_single_egld_transfer(capsys: Any): - return_code = main([ - "tx", "new", - "--pem", str(testdata_path / "alice.pem"), - "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--nonce", "7", - "--gas-limit", "1300000", - "--token-transfers", "EGLD-000000", "1000000000000000000", - "--chain", "T", - ]) + return_code = main( + [ + "tx", + "new", + "--pem", + str(testdata_path / "alice.pem"), + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", + "7", + "--gas-limit", + "1300000", + "--token-transfers", + "EGLD-000000", + "1000000000000000000", + "--chain", + "T", + ] + ) assert return_code == 0 tx = _read_stdout(capsys) tx_json = json.loads(tx) data = tx_json["emittedTransactionData"] - assert data == "MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@01@45474c442d303030303030@@0de0b6b3a7640000" + assert ( + data + == "MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@01@45474c442d303030303030@@0de0b6b3a7640000" + ) def test_relayed_v3_without_relayer_wallet(capsys: Any): - return_code = main([ - "tx", "new", - "--pem", str(testdata_path / "alice.pem"), - "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--nonce", "7", - "--gas-limit", "1300000", - "--value", "1000000000000000000", - "--chain", "T", - "--relayer", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" - ]) + return_code = main( + [ + "tx", + "new", + "--pem", + str(testdata_path / "alice.pem"), + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", + "7", + "--gas-limit", + "1300000", + "--value", + "1000000000000000000", + "--chain", + "T", + "--relayer", + "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + ] + ) assert return_code == 0 tx = _read_stdout(capsys) tx_json = json.loads(tx)["emittedTransaction"] @@ -127,17 +209,28 @@ def test_relayed_v3_without_relayer_wallet(capsys: Any): def test_relayed_v3_incorrect_relayer(): - return_code = main([ - "tx", "new", - "--pem", str(testdata_path / "alice.pem"), - "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--nonce", "7", - "--gas-limit", "1300000", - "--value", "1000000000000000000", - "--chain", "T", - "--relayer", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", - "--relayer-pem", str(testdata_path / "alice.pem") - ]) + return_code = main( + [ + "tx", + "new", + "--pem", + str(testdata_path / "alice.pem"), + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", + "7", + "--gas-limit", + "1300000", + "--value", + "1000000000000000000", + "--chain", + "T", + "--relayer", + "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--relayer-pem", + str(testdata_path / "alice.pem"), + ] + ) assert return_code @@ -145,17 +238,28 @@ def test_create_relayed_v3_transaction(capsys: Any): # create relayed v3 tx and save signature and relayer signature # create the same tx, save to file # sign from file with relayer wallet and make sure signatures match - return_code = main([ - "tx", "new", - "--pem", str(testdata_path / "alice.pem"), - "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--nonce", "7", - "--gas-limit", "1300000", - "--value", "1000000000000000000", - "--chain", "T", - "--relayer", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", - "--relayer-pem", str(testdata_path / "testUser.pem") - ]) + return_code = main( + [ + "tx", + "new", + "--pem", + str(testdata_path / "alice.pem"), + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", + "7", + "--gas-limit", + "1300000", + "--value", + "1000000000000000000", + "--chain", + "T", + "--relayer", + "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--relayer-pem", + str(testdata_path / "testUser.pem"), + ] + ) assert return_code == 0 tx = _read_stdout(capsys) @@ -173,27 +277,43 @@ def test_create_relayed_v3_transaction(capsys: Any): capsys.readouterr() # save tx to file then load and sign tx by relayer - return_code = main([ - "tx", "new", - "--pem", str(testdata_path / "alice.pem"), - "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--nonce", "7", - "--gas-limit", "1300000", - "--value", "1000000000000000000", - "--chain", "T", - "--relayer", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", - "--outfile", str(testdata_out / "relayed.json") - ]) + return_code = main( + [ + "tx", + "new", + "--pem", + str(testdata_path / "alice.pem"), + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", + "7", + "--gas-limit", + "1300000", + "--value", + "1000000000000000000", + "--chain", + "T", + "--relayer", + "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--outfile", + str(testdata_out / "relayed.json"), + ] + ) assert return_code == 0 # Clear the captured content capsys.readouterr() - return_code = main([ - "tx", "relay", - "--relayer-pem", str(testdata_path / "testUser.pem"), - "--infile", str(testdata_out / "relayed.json") - ]) + return_code = main( + [ + "tx", + "relay", + "--relayer-pem", + str(testdata_path / "testUser.pem"), + "--infile", + str(testdata_out / "relayed.json"), + ] + ) assert return_code == 0 tx = _read_stdout(capsys) @@ -206,10 +326,7 @@ def test_create_relayed_v3_transaction(capsys: Any): def test_check_relayer_wallet_is_provided(): - return_code = main([ - "tx", "relay", - "--infile", str(testdata_out / "relayed.json") - ]) + return_code = main(["tx", "relay", "--infile", str(testdata_out / "relayed.json")]) assert return_code diff --git a/multiversx_sdk_cli/tests/test_cli_validators.py b/multiversx_sdk_cli/tests/test_cli_validators.py index 8b7cc521..04576f94 100644 --- a/multiversx_sdk_cli/tests/test_cli_validators.py +++ b/multiversx_sdk_cli/tests/test_cli_validators.py @@ -1,7 +1,7 @@ -import pytest - from pathlib import Path +import pytest + from multiversx_sdk_cli.cli import main testdata_path = Path(__file__).parent / "testdata" @@ -18,181 +18,290 @@ def test_stake(): validators_json = testdata_path / "validators_ci.json" # Stake with recall nonce - return_code = main([ - "validator", "stake", - "--pem", str(alice_pem), - "--value", "2500000000000000000000", - "--validators-file", str(validators_json), - "--reward-address", reward_address, - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "stake", + "--pem", + str(alice_pem), + "--value", + "2500000000000000000000", + "--validators-file", + str(validators_json), + "--reward-address", + reward_address, + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 # Stake with provided nonce - return_code = main([ - "validator", "stake", - "--pem", str(alice_pem), - "--value", "2500000000000000000000", - "--validators-file", str(validators_json), - "--reward-address", reward_address, - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--nonce=0" - ]) + return_code = main( + [ + "validator", + "stake", + "--pem", + str(alice_pem), + "--value", + "2500000000000000000000", + "--validators-file", + str(validators_json), + "--reward-address", + reward_address, + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--nonce=0", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_stake_top_up(): # Stake with topUp - return_code = main([ - "validator", "stake", "--top-up", - "--pem", str(alice_pem), - "--value", "2711000000000000000000", - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "stake", + "--top-up", + "--pem", + str(alice_pem), + "--value", + "2711000000000000000000", + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_unstake(): # Unstake - return_code = main([ - "validator", "unstake", - "--pem", str(alice_pem), - "--nodes-public-key", bls_key, - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "unstake", + "--pem", + str(alice_pem), + "--nodes-public-key", + bls_key, + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_unbond(): # Unbond - return_code = main([ - "validator", "unbond", - "--pem", str(alice_pem), - "--nodes-public-key", bls_key, - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "unbond", + "--pem", + str(alice_pem), + "--nodes-public-key", + bls_key, + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_unjail(): # Unjail - return_code = main([ - "validator", "unjail", - "--pem", str(alice_pem), - "--value", "2500000000000000000000", - "--nodes-public-key", bls_key, - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "unjail", + "--pem", + str(alice_pem), + "--value", + "2500000000000000000000", + "--nodes-public-key", + bls_key, + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_change_reward_address(): # Change reward address - return_code = main([ - "validator", "change-reward-address", - "--pem", str(alice_pem), - "--reward-address", reward_address, - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "change-reward-address", + "--pem", + str(alice_pem), + "--reward-address", + reward_address, + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_unstake_nodes(): # Unstake Nodes - return_code = main([ - "validator", "unstake-nodes", - "--pem", str(alice_pem), - "--nodes-public-key", bls_key, - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "unstake-nodes", + "--pem", + str(alice_pem), + "--nodes-public-key", + bls_key, + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_unstake_tokens(): # Unstake Tokens - return_code = main([ - "validator", "unstake-tokens", - "--pem", str(alice_pem), - "--unstake-value", "11000000000000000000", - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "unstake-tokens", + "--pem", + str(alice_pem), + "--unstake-value", + "11000000000000000000", + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_unbond_nodes(): # Unbond nodes - return_code = main([ - "validator", "unbond-nodes", - "--pem", str(alice_pem), - "--nodes-public-keys", bls_key, - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "unbond-nodes", + "--pem", + str(alice_pem), + "--nodes-public-keys", + bls_key, + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_unbond_tokens(): # Unbond nodes - return_code = main([ - "validator", "unbond-tokens", - "--pem", str(alice_pem), - "--unbond-value", "20000000000000000000", - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "unbond-tokens", + "--pem", + str(alice_pem), + "--unbond-value", + "20000000000000000000", + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_clean_registration_data(): # Clean registration data - return_code = main([ - "validator", "clean-registered-data", - "--pem", str(alice_pem), - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "clean-registered-data", + "--pem", + str(alice_pem), + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 @pytest.mark.require_localnet def test_re_stake_unstaked_nodes(): # Clean registration data - return_code = main([ - "validator", "restake-unstaked-nodes", - "--pem", str(alice_pem), - "--nodes-public-keys", bls_key, - "--chain", "localnet", - "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce" - ]) + return_code = main( + [ + "validator", + "restake-unstaked-nodes", + "--pem", + str(alice_pem), + "--nodes-public-keys", + bls_key, + "--chain", + "localnet", + "--proxy", + "http://127.0.0.1:7950", + "--estimate-gas", + "--recall-nonce", + ] + ) assert return_code == 0 diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index 6121dbcc..bea4b6f6 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -3,8 +3,7 @@ from pathlib import Path from typing import Any -from multiversx_sdk import (Address, AddressComputer, Mnemonic, UserPEM, - UserWallet) +from multiversx_sdk import Address, AddressComputer, Mnemonic, UserPEM, UserWallet from multiversx_sdk_cli.cli import main @@ -60,11 +59,7 @@ def test_wallet_new_as_mnemonic(): outfile = testdata_out_path / "wallet.txt" outfile.unlink(missing_ok=True) - main([ - "wallet", "new", - "--format", "raw-mnemonic", - "--outfile", str(outfile) - ]) + main(["wallet", "new", "--format", "raw-mnemonic", "--outfile", str(outfile)]) assert Mnemonic.is_text_valid(outfile.read_text()) @@ -73,23 +68,35 @@ def test_wallet_new_as_pem(): outfile = testdata_out_path / "wallet.pem" outfile.unlink(missing_ok=True) - main([ - "wallet", "new", - "--format", "pem", - "--outfile", str(outfile), - "--address-hrp", "erd" - ]) + main( + [ + "wallet", + "new", + "--format", + "pem", + "--outfile", + str(outfile), + "--address-hrp", + "erd", + ] + ) assert UserPEM.from_file(outfile).label.startswith("erd1") outfile.unlink(missing_ok=True) - main([ - "wallet", "new", - "--format", "pem", - "--outfile", str(outfile), - "--address-hrp", "test" - ]) + main( + [ + "wallet", + "new", + "--format", + "pem", + "--outfile", + str(outfile), + "--address-hrp", + "test", + ] + ) assert UserPEM.from_file(outfile).label.startswith("test1") @@ -99,11 +106,7 @@ def test_wallet_new_as_keystore_with_mnemonic(capsys: Any, monkeypatch: Any): outfile.unlink(missing_ok=True) _mock_getpass(monkeypatch, "password") - main([ - "wallet", "new", - "--format", "keystore-mnemonic", - "--outfile", str(outfile) - ]) + main(["wallet", "new", "--format", "keystore-mnemonic", "--outfile", str(outfile)]) expected_mnemonic = _read_stdout_mnemonic(capsys) keyfile = json.loads(outfile.read_text()) @@ -116,11 +119,7 @@ def test_wallet_new_as_keystore_with_secret_key(capsys: Any, monkeypatch: Any): outfile.unlink(missing_ok=True) _mock_getpass(monkeypatch, "password") - main([ - "wallet", "new", - "--format", "keystore-secret-key", - "--outfile", str(outfile) - ]) + main(["wallet", "new", "--format", "keystore-secret-key", "--outfile", str(outfile)]) expected_secret_key = Mnemonic(_read_stdout_mnemonic(capsys)).derive_key(0) actual_secret_key = UserWallet.load_secret_key(outfile, "password") @@ -132,12 +131,22 @@ def test_wallet_convert_raw_mnemonic_to_pem(): outfile = testdata_out_path / "alice.pem" outfile.unlink(missing_ok=True) - main([ - "wallet", "convert", - "--in-format", "raw-mnemonic", "--infile", str(infile), - "--out-format", "pem", "--outfile", str(outfile), - "--address-index", "0" - ]) + main( + [ + "wallet", + "convert", + "--in-format", + "raw-mnemonic", + "--infile", + str(infile), + "--out-format", + "pem", + "--outfile", + str(outfile), + "--address-index", + "0", + ] + ) pem = UserPEM.from_file(outfile) assert pem.label == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" @@ -151,11 +160,20 @@ def test_wallet_convert_raw_mnemonic_to_keystore_with_mnemonic(monkeypatch: Any) outfile.unlink(missing_ok=True) _mock_getpass(monkeypatch, "password") - main([ - "wallet", "convert", - "--in-format", "raw-mnemonic", "--infile", str(infile), - "--out-format", "keystore-mnemonic", "--outfile", str(outfile) - ]) + main( + [ + "wallet", + "convert", + "--in-format", + "raw-mnemonic", + "--infile", + str(infile), + "--out-format", + "keystore-mnemonic", + "--outfile", + str(outfile), + ] + ) keyfile_json = outfile.read_text() keyfile = json.loads(keyfile_json) @@ -171,12 +189,22 @@ def test_wallet_convert_raw_mnemonic_to_keystore_with_secret_key(monkeypatch: An outfile.unlink(missing_ok=True) _mock_getpass(monkeypatch, "password") - main([ - "wallet", "convert", - "--in-format", "raw-mnemonic", "--infile", str(infile), - "--out-format", "keystore-secret-key", "--outfile", str(outfile), - "--address-index", "0" - ]) + main( + [ + "wallet", + "convert", + "--in-format", + "raw-mnemonic", + "--infile", + str(infile), + "--out-format", + "keystore-secret-key", + "--outfile", + str(outfile), + "--address-index", + "0", + ] + ) keyfile_json = outfile.read_text() keyfile = json.loads(keyfile_json) @@ -185,12 +213,22 @@ def test_wallet_convert_raw_mnemonic_to_keystore_with_secret_key(monkeypatch: An # Bob outfile.unlink(missing_ok=True) - main([ - "wallet", "convert", - "--in-format", "raw-mnemonic", "--infile", str(infile), - "--out-format", "keystore-secret-key", "--outfile", str(outfile), - "--address-index", "1" - ]) + main( + [ + "wallet", + "convert", + "--in-format", + "raw-mnemonic", + "--infile", + str(infile), + "--out-format", + "keystore-secret-key", + "--outfile", + str(outfile), + "--address-index", + "1", + ] + ) keyfile_json = outfile.read_text() keyfile = json.loads(keyfile_json) @@ -205,11 +243,20 @@ def test_wallet_convert_keystore_with_secret_key_to_pem(monkeypatch: Any): outfile.unlink(missing_ok=True) _mock_getpass(monkeypatch, "password") - main([ - "wallet", "convert", - "--in-format", "keystore-secret-key", "--infile", str(infile), - "--out-format", "pem", "--outfile", str(outfile) - ]) + main( + [ + "wallet", + "convert", + "--in-format", + "keystore-secret-key", + "--infile", + str(infile), + "--out-format", + "pem", + "--outfile", + str(outfile), + ] + ) pem = UserPEM.from_file(outfile) assert pem.label == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" @@ -217,18 +264,28 @@ def test_wallet_convert_keystore_with_secret_key_to_pem(monkeypatch: Any): def test_wallet_bech32_encode(capsys: Any): - main([ - "wallet", "bech32", "--encode", "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1" - ]) + main( + [ + "wallet", + "bech32", + "--encode", + "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", + ] + ) out = _read_stdout(capsys) assert out == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" def test_wallet_bech32_decode(capsys: Any): - main([ - "wallet", "bech32", "--decode", "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" - ]) + main( + [ + "wallet", + "bech32", + "--decode", + "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + ] + ) out = _read_stdout(capsys) assert out == "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1" @@ -237,9 +294,18 @@ def test_wallet_bech32_decode(capsys: Any): def test_wallet_convert_pem_to_bech32_address(capsys: Any): infile = testdata_path / "alice.pem" - main([ - "wallet", "convert", "--infile", str(infile), "--in-format", "pem", "--out-format", "address-bech32" - ]) + main( + [ + "wallet", + "convert", + "--infile", + str(infile), + "--in-format", + "pem", + "--out-format", + "address-bech32", + ] + ) out = _read_stdout(capsys).strip("Output:\n\n") assert out == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" @@ -248,9 +314,18 @@ def test_wallet_convert_pem_to_bech32_address(capsys: Any): def test_wallet_convert_pem_to_pubkey(capsys: Any): infile = testdata_path / "alice.pem" - main([ - "wallet", "convert", "--infile", str(infile), "--in-format", "pem", "--out-format", "address-hex" - ]) + main( + [ + "wallet", + "convert", + "--infile", + str(infile), + "--in-format", + "pem", + "--out-format", + "address-hex", + ] + ) out = _read_stdout(capsys).strip("Output:\n\n") assert out == "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1" @@ -267,7 +342,7 @@ def test_wallet_sign_message(capsys: Any): assert out == { "address": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", "message": "test", - "signature": "0x7aff43cd6e3d880a65033bf0a1b16274854fd7dfa9fe5faa7fa9a665ee851afd4c449310f5f1697d348e42d1819eaef69080e33e7652d7393521ed50d7427a0e" + "signature": "0x7aff43cd6e3d880a65033bf0a1b16274854fd7dfa9fe5faa7fa9a665ee851afd4c449310f5f1697d348e42d1819eaef69080e33e7652d7393521ed50d7427a0e", } @@ -276,16 +351,18 @@ def test_verify_previously_signed_message(capsys: Any): address = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" signature = "0x7aff43cd6e3d880a65033bf0a1b16274854fd7dfa9fe5faa7fa9a665ee851afd4c449310f5f1697d348e42d1819eaef69080e33e7652d7393521ed50d7427a0e" - return_code = main([ - "wallet", - "verify-message", - "--address", - address, - "--message", - message, - "--signature", - signature - ]) + return_code = main( + [ + "wallet", + "verify-message", + "--address", + address, + "--message", + message, + "--signature", + signature, + ] + ) assert False if return_code else True out = _read_stdout(capsys) @@ -298,16 +375,18 @@ def test_verify_not_signed_message(capsys: Any): address = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" signature = "0x7aff43cd6e3d880a65033bf0a1b16274854fd7dfa9fe5faa7fa9a665ee851afd4c449310f5f1697d348e42d1819eaef69080e33e7652d7393521ed50d7427a0e" - return_code = main([ - "wallet", - "verify-message", - "--address", - address, - "--message", - message, - "--signature", - signature - ]) + return_code = main( + [ + "wallet", + "verify-message", + "--address", + address, + "--message", + message, + "--signature", + signature, + ] + ) assert False if return_code else True out = _read_stdout(capsys) @@ -319,63 +398,117 @@ def test_sign_and_verify_message_with_multi_address_pem(capsys: Any): multi_address_pem_path = testdata_path / "multiple_addresses.pem" message = "test" - return_code = main(["wallet", "sign-message", "--message", message, "--pem", str(multi_address_pem_path), "--pem-index", "0"]) + return_code = main( + [ + "wallet", + "sign-message", + "--message", + message, + "--pem", + str(multi_address_pem_path), + "--pem-index", + "0", + ] + ) out = json.loads(_read_stdout(capsys)) assert False if return_code else True assert out == { "address": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", "message": "test", - "signature": "0x7aff43cd6e3d880a65033bf0a1b16274854fd7dfa9fe5faa7fa9a665ee851afd4c449310f5f1697d348e42d1819eaef69080e33e7652d7393521ed50d7427a0e" + "signature": "0x7aff43cd6e3d880a65033bf0a1b16274854fd7dfa9fe5faa7fa9a665ee851afd4c449310f5f1697d348e42d1819eaef69080e33e7652d7393521ed50d7427a0e", } - return_code = main(["wallet", "verify-message", - "--address", "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", - "--message", message, - "--signature", "0x7aff43cd6e3d880a65033bf0a1b16274854fd7dfa9fe5faa7fa9a665ee851afd4c449310f5f1697d348e42d1819eaef69080e33e7652d7393521ed50d7427a0e" - ]) + return_code = main( + [ + "wallet", + "verify-message", + "--address", + "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + "--message", + message, + "--signature", + "0x7aff43cd6e3d880a65033bf0a1b16274854fd7dfa9fe5faa7fa9a665ee851afd4c449310f5f1697d348e42d1819eaef69080e33e7652d7393521ed50d7427a0e", + ] + ) assert False if return_code else True out = _read_stdout(capsys) text = """SUCCESS: The message "test" was signed by erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th""".split() assert all(word in out for word in text) - return_code = main(["wallet", "sign-message", "--message", message, "--pem", str(multi_address_pem_path), "--pem-index", "1"]) + return_code = main( + [ + "wallet", + "sign-message", + "--message", + message, + "--pem", + str(multi_address_pem_path), + "--pem-index", + "1", + ] + ) out = json.loads(_read_stdout(capsys)) assert False if return_code else True assert out == { "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", "message": "test", - "signature": "0x50024bd755f8801e4ffbe76f93e6b8d82220673981cc44952a52bbfa02d262020ce933a2216e0e16e838dc5691f5701a1e0c250279cdae29268344d1523ea805" + "signature": "0x50024bd755f8801e4ffbe76f93e6b8d82220673981cc44952a52bbfa02d262020ce933a2216e0e16e838dc5691f5701a1e0c250279cdae29268344d1523ea805", } - return_code = main(["wallet", "verify-message", - "--address", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--message", message, - "--signature", "0x50024bd755f8801e4ffbe76f93e6b8d82220673981cc44952a52bbfa02d262020ce933a2216e0e16e838dc5691f5701a1e0c250279cdae29268344d1523ea805" - ]) + return_code = main( + [ + "wallet", + "verify-message", + "--address", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--message", + message, + "--signature", + "0x50024bd755f8801e4ffbe76f93e6b8d82220673981cc44952a52bbfa02d262020ce933a2216e0e16e838dc5691f5701a1e0c250279cdae29268344d1523ea805", + ] + ) assert False if return_code else True out = _read_stdout(capsys) text = """SUCCESS: The message "test" was signed by erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx""".split() assert all(word in out for word in text) - return_code = main(["wallet", "sign-message", "--message", message, "--pem", str(multi_address_pem_path), "--pem-index", "2"]) + return_code = main( + [ + "wallet", + "sign-message", + "--message", + message, + "--pem", + str(multi_address_pem_path), + "--pem-index", + "2", + ] + ) out = json.loads(_read_stdout(capsys)) assert False if return_code else True assert out == { "address": "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8", "message": "test", - "signature": "0x99dd1ebf2bf47a7cb99f362d2458b5c83c727686517c97c877babeb8be7f840c543785edf2f9688a1fc2c076b0887d8ee6d2be7b181bba4dc3984a4ee406fa0f" + "signature": "0x99dd1ebf2bf47a7cb99f362d2458b5c83c727686517c97c877babeb8be7f840c543785edf2f9688a1fc2c076b0887d8ee6d2be7b181bba4dc3984a4ee406fa0f", } - return_code = main(["wallet", "verify-message", - "--address", "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8", - "--message", message, - "--signature", "0x99dd1ebf2bf47a7cb99f362d2458b5c83c727686517c97c877babeb8be7f840c543785edf2f9688a1fc2c076b0887d8ee6d2be7b181bba4dc3984a4ee406fa0f" - ]) + return_code = main( + [ + "wallet", + "verify-message", + "--address", + "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8", + "--message", + message, + "--signature", + "0x99dd1ebf2bf47a7cb99f362d2458b5c83c727686517c97c877babeb8be7f840c543785edf2f9688a1fc2c076b0887d8ee6d2be7b181bba4dc3984a4ee406fa0f", + ] + ) assert False if return_code else True out = _read_stdout(capsys) diff --git a/multiversx_sdk_cli/tests/test_contracts.py b/multiversx_sdk_cli/tests/test_contracts.py index 671c9a02..5e70c90a 100644 --- a/multiversx_sdk_cli/tests/test_contracts.py +++ b/multiversx_sdk_cli/tests/test_contracts.py @@ -21,24 +21,25 @@ def test_playground_keccak(): def test_prepare_argument(): - assert _prepare_argument('0x5') == '05' - assert _prepare_argument('5') == '05' - assert _prepare_argument('0x05f') == '005F' - assert _prepare_argument('0xaaa') == '0AAA' - assert _prepare_argument('str:a') == '61' - assert _prepare_argument('str:aaa') == '616161' - - assert _prepare_argument(155) == '9B' - assert _prepare_argument('155') == '9B' - - assert \ - _prepare_argument('erd1qr9av6ar4ymr05xj93jzdxyezdrp6r4hz6u0scz4dtzvv7kmlldse7zktc') == \ - '00CBD66BA3A93637D0D22C6426989913461D0EB716B8F860556AC4C67ADBFFDB' - - assert _prepare_argument('str:TOK-123456') == '544F4B2D313233343536' - assert _prepare_argument('str:TOK-a1c2ef') == '544F4B2D613163326566' - assert _prepare_argument('str:TokenName') == '546F6B656E4E616D65' - assert _prepare_argument('str:/#%placeholder&*') == '2F2325706C616365686F6C646572262A' + assert _prepare_argument("0x5") == "05" + assert _prepare_argument("5") == "05" + assert _prepare_argument("0x05f") == "005F" + assert _prepare_argument("0xaaa") == "0AAA" + assert _prepare_argument("str:a") == "61" + assert _prepare_argument("str:aaa") == "616161" + + assert _prepare_argument(155) == "9B" + assert _prepare_argument("155") == "9B" + + assert ( + _prepare_argument("erd1qr9av6ar4ymr05xj93jzdxyezdrp6r4hz6u0scz4dtzvv7kmlldse7zktc") + == "00CBD66BA3A93637D0D22C6426989913461D0EB716B8F860556AC4C67ADBFFDB" + ) + + assert _prepare_argument("str:TOK-123456") == "544F4B2D313233343536" + assert _prepare_argument("str:TOK-a1c2ef") == "544F4B2D613163326566" + assert _prepare_argument("str:TokenName") == "546F6B656E4E616D65" + assert _prepare_argument("str:/#%placeholder&*") == "2F2325706C616365686F6C646572262A" assert _prepare_argument(True) == "01" assert _prepare_argument(False) == "00" @@ -46,7 +47,7 @@ def test_prepare_argument(): assert _prepare_argument("fAlSe") == "00" with pytest.raises(errors.UnknownArgumentFormat): - _ = _prepare_argument('0x05fq') + _ = _prepare_argument("0x05fq") assert _prepare_argument("str:") == "" assert _prepare_argument("0x") == "" @@ -58,15 +59,21 @@ def test_contract_verification_create_request_signature(): request_payload = b"test" signature = _create_request_signature(account, contract_address, request_payload) - assert signature.hex() == "30111258cc42ea08e0c6a3e053cc7086a88d614b8b119a244904e9a19896c73295b2fe5c520a1cb07cfe20f687deef9f294a0a05071e85c78a70a448ea5f0605" + assert ( + signature.hex() + == "30111258cc42ea08e0c6a3e053cc7086a88d614b8b119a244904e9a19896c73295b2fe5c520a1cb07cfe20f687deef9f294a0a05071e85c78a70a448ea5f0605" + ) def test_prepare_args_for_factories(): sc = SmartContract(TransactionsFactoryConfig("mock")) args = [ - "0x5", "123", "false", "true", + "0x5", + "123", + "false", + "true", "str:test-string", - "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", ] arguments = sc._prepare_args_for_factory(args) @@ -75,4 +82,7 @@ def test_prepare_args_for_factories(): assert arguments[2].get_payload() is False assert arguments[3].get_payload() is True assert arguments[4].get_payload() == "test-string" - assert arguments[5].get_payload() == Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th").get_public_key() + assert ( + arguments[5].get_payload() + == Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th").get_public_key() + ) diff --git a/multiversx_sdk_cli/tests/test_native_auth_client.py b/multiversx_sdk_cli/tests/test_native_auth_client.py index 92678941..93800cd8 100644 --- a/multiversx_sdk_cli/tests/test_native_auth_client.py +++ b/multiversx_sdk_cli/tests/test_native_auth_client.py @@ -2,8 +2,10 @@ import pytest -from multiversx_sdk_cli.native_auth_client import (NativeAuthClient, - NativeAuthClientConfig) +from multiversx_sdk_cli.native_auth_client import ( + NativeAuthClient, + NativeAuthClientConfig, +) def mock(mocker: Any, code: int, response: Any): @@ -25,14 +27,14 @@ def side_effect(*args: Any, **kwargs: Any): class TestNativeAuth: - ADDRESS = 'erd1qnk2vmuqywfqtdnkmauvpm8ls0xh00k8xeupuaf6cm6cd4rx89qqz0ppgl' - SIGNATURE = '906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d' - BLOCK_HASH = 'ab459013b27fdc6fe98eed567bd0c1754e0628a4cc16883bf0170a29da37ad46' + ADDRESS = "erd1qnk2vmuqywfqtdnkmauvpm8ls0xh00k8xeupuaf6cm6cd4rx89qqz0ppgl" + SIGNATURE = "906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d" + BLOCK_HASH = "ab459013b27fdc6fe98eed567bd0c1754e0628a4cc16883bf0170a29da37ad46" TTL = 86400 - ORIGIN = 'https://api.multiversx.com' + ORIGIN = "https://api.multiversx.com" TOKEN = f"aHR0cHM6Ly9hcGkubXVsdGl2ZXJzeC5jb20.{BLOCK_HASH}.{TTL}.e30" - ACCESS_TOKEN = 'ZXJkMXFuazJ2bXVxeXdmcXRkbmttYXV2cG04bHMweGgwMGs4eGV1cHVhZjZjbTZjZDRyeDg5cXF6MHBwZ2w.YUhSMGNITTZMeTloY0drdWJYVnNkR2wyWlhKemVDNWpiMjAuYWI0NTkwMTNiMjdmZGM2ZmU5OGVlZDU2N2JkMGMxNzU0ZTA2MjhhNGNjMTY4ODNiZjAxNzBhMjlkYTM3YWQ0Ni44NjQwMC5lMzA.906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d' - INVALID_HASH_ERROR = 'Validation failed for block hash \'hash\'. Length should be 64.' + ACCESS_TOKEN = "ZXJkMXFuazJ2bXVxeXdmcXRkbmttYXV2cG04bHMweGgwMGs4eGV1cHVhZjZjbTZjZDRyeDg5cXF6MHBwZ2w.YUhSMGNITTZMeTloY0drdWJYVnNkR2wyWlhKemVDNWpiMjAuYWI0NTkwMTNiMjdmZGM2ZmU5OGVlZDU2N2JkMGMxNzU0ZTA2MjhhNGNjMTY4ODNiZjAxNzBhMjlkYTM3YWQ0Ni44NjQwMC5lMzA.906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d" + INVALID_HASH_ERROR = "Validation failed for block hash 'hash'. Length should be 64." def test_latest_block_should_return_signable_token(self, mocker: Any): mock(mocker, 200, [{"hash": self.BLOCK_HASH}]) @@ -49,9 +51,17 @@ def test_throws_internal_server_error(self, mocker: Any): # if `/blocks/latest` raises error should fallback to `/blocks?size=1` def test_fallback_mechanism(self, mocker: Any): - mock(mocker, 400, [{"statusCode": 400, - "message": self.INVALID_HASH_ERROR, - "error": "Bad request"}]) + mock( + mocker, + 400, + [ + { + "statusCode": 400, + "message": self.INVALID_HASH_ERROR, + "error": "Bad request", + } + ], + ) mock(mocker, 200, {"hash": self.BLOCK_HASH}) config = NativeAuthClientConfig(origin=self.ORIGIN, expiry_seconds=self.TTL) @@ -67,32 +77,46 @@ def test_generate_access_token(self): class TestNativeAuthWithGateway: - ADDRESS = 'erd1qnk2vmuqywfqtdnkmauvpm8ls0xh00k8xeupuaf6cm6cd4rx89qqz0ppgl' - SIGNATURE = '906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d' - BLOCK_HASH = 'ab459013b27fdc6fe98eed567bd0c1754e0628a4cc16883bf0170a29da37ad46' + ADDRESS = "erd1qnk2vmuqywfqtdnkmauvpm8ls0xh00k8xeupuaf6cm6cd4rx89qqz0ppgl" + SIGNATURE = "906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d" + BLOCK_HASH = "ab459013b27fdc6fe98eed567bd0c1754e0628a4cc16883bf0170a29da37ad46" TTL = 86400 - ORIGIN = 'https://api.multiversx.com' + ORIGIN = "https://api.multiversx.com" TOKEN = f"aHR0cHM6Ly9hcGkubXVsdGl2ZXJzeC5jb20.{BLOCK_HASH}.{TTL}.e30" - ACCESS_TOKEN = 'ZXJkMXFuazJ2bXVxeXdmcXRkbmttYXV2cG04bHMweGgwMGs4eGV1cHVhZjZjbTZjZDRyeDg5cXF6MHBwZ2w.YUhSMGNITTZMeTloY0drdWJYVnNkR2wyWlhKemVDNWpiMjAuYWI0NTkwMTNiMjdmZGM2ZmU5OGVlZDU2N2JkMGMxNzU0ZTA2MjhhNGNjMTY4ODNiZjAxNzBhMjlkYTM3YWQ0Ni44NjQwMC5lMzA.906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d' + ACCESS_TOKEN = "ZXJkMXFuazJ2bXVxeXdmcXRkbmttYXV2cG04bHMweGgwMGs4eGV1cHVhZjZjbTZjZDRyeDg5cXF6MHBwZ2w.YUhSMGNITTZMeTloY0drdWJYVnNkR2wyWlhKemVDNWpiMjAuYWI0NTkwMTNiMjdmZGM2ZmU5OGVlZDU2N2JkMGMxNzU0ZTA2MjhhNGNjMTY4ODNiZjAxNzBhMjlkYTM3YWQ0Ni44NjQwMC5lMzA.906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d" LATEST_ROUND = 115656 METASHARD = 4294967295 - GATEWAY = 'https://gateway.multiversx.com' + GATEWAY = "https://gateway.multiversx.com" def test_latest_block_should_return_signable_token(self, mocker: Any): responses = [ - {"code": 200, "response": {"data": {"status": {"erd_current_round": self.LATEST_ROUND}}}}, - {"code": 200, "response": {"data": {"blocks": [{"shard": self.METASHARD, "hash": self.BLOCK_HASH}]}}} + { + "code": 200, + "response": {"data": {"status": {"erd_current_round": self.LATEST_ROUND}}}, + }, + { + "code": 200, + "response": {"data": {"blocks": [{"shard": self.METASHARD, "hash": self.BLOCK_HASH}]}}, + }, ] mock_side_effect(mocker, responses) - config = NativeAuthClientConfig(origin=self.ORIGIN, gateway_url=self.GATEWAY, block_hash_shard=self.METASHARD, expiry_seconds=self.TTL) + config = NativeAuthClientConfig( + origin=self.ORIGIN, + gateway_url=self.GATEWAY, + block_hash_shard=self.METASHARD, + expiry_seconds=self.TTL, + ) client = NativeAuthClient(config) token = client.initialize() assert token == self.TOKEN def test_should_raise_internal_server_error(self, mocker: Any): responses = [ - {"code": 500, "response": {"data": {"status": {"erd_current_round": self.LATEST_ROUND}}}} + { + "code": 500, + "response": {"data": {"status": {"erd_current_round": self.LATEST_ROUND}}}, + } ] mock_side_effect(mocker, responses) @@ -104,8 +128,11 @@ def test_should_raise_internal_server_error(self, mocker: Any): def test_raises_internal_server_error_on_second_request(self, mocker: Any): responses = [ - {"code": 200, "response": {"data": {"status": {"erd_current_round": self.LATEST_ROUND}}}}, - {"code": 500, "response": {""}} + { + "code": 200, + "response": {"data": {"status": {"erd_current_round": self.LATEST_ROUND}}}, + }, + {"code": 500, "response": {""}}, ] mock_side_effect(mocker, responses) @@ -119,10 +146,6 @@ def test_generate_access_token(self): config = NativeAuthClientConfig(gateway_url=self.GATEWAY, block_hash_shard=self.METASHARD) client = NativeAuthClient(config) - access_token = client.get_token( - address=self.ADDRESS, - token=self.TOKEN, - signature=self.SIGNATURE - ) + access_token = client.get_token(address=self.ADDRESS, token=self.TOKEN, signature=self.SIGNATURE) assert access_token == self.ACCESS_TOKEN diff --git a/multiversx_sdk_cli/tests/test_playground_proxy.py b/multiversx_sdk_cli/tests/test_playground_proxy.py index 3bd999ee..29d0851d 100644 --- a/multiversx_sdk_cli/tests/test_playground_proxy.py +++ b/multiversx_sdk_cli/tests/test_playground_proxy.py @@ -3,7 +3,7 @@ class TestPlaygroundProxy: - @pytest.mark.skip('manual run only') + @pytest.mark.skip("manual run only") def test_do_request(self): # use a valid proxy address url = "http://localhost:8001" @@ -14,7 +14,7 @@ def test_do_request(self): print(response.json()) assert response is not None - @pytest.mark.skip('manual run only') + @pytest.mark.skip("manual run only") def test_do_request_node_status(self): # use a valid proxy address url = "http://localhost:8001" diff --git a/multiversx_sdk_cli/tests/test_proxy.py b/multiversx_sdk_cli/tests/test_proxy.py index 8234f7fb..947af319 100644 --- a/multiversx_sdk_cli/tests/test_proxy.py +++ b/multiversx_sdk_cli/tests/test_proxy.py @@ -13,7 +13,7 @@ def test_get_account(): "--address", "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", "--proxy", - "https://testnet-api.multiversx.com" + "https://testnet-api.multiversx.com", ] ) assert False if result else True diff --git a/multiversx_sdk_cli/tests/test_sign.py b/multiversx_sdk_cli/tests/test_sign.py index 77bc8bba..14870a99 100644 --- a/multiversx_sdk_cli/tests/test_sign.py +++ b/multiversx_sdk_cli/tests/test_sign.py @@ -1,5 +1,6 @@ import json from pathlib import Path + from multiversx_sdk_cli.cli import main @@ -9,16 +10,18 @@ def test_sign_tx(): signed_transaction = parent / "testdata-out" / "signed_transaction.json" expected_signature = "7b0fa3bd477a9aacdfd8d6b41628e525afbbc94b4b56c2a30a10f78514c2f6558b27eef701633481f1ef54b62697c91e9dc06cc6d2038bd13cf9557467142005" - main([ - "tx", - "sign", - "--pem", - f"{parent}/testdata/testUser.pem", - "--infile", - f"{unsigned_transaction}", - "--outfile", - f"{signed_transaction}" - ]) + main( + [ + "tx", + "sign", + "--pem", + f"{parent}/testdata/testUser.pem", + "--infile", + f"{unsigned_transaction}", + "--outfile", + f"{signed_transaction}", + ] + ) with open(signed_transaction) as f: signed_tx = json.load(f) diff --git a/multiversx_sdk_cli/tests/test_testnet.py b/multiversx_sdk_cli/tests/test_testnet.py index 40c999fe..4f29b83f 100644 --- a/multiversx_sdk_cli/tests/test_testnet.py +++ b/multiversx_sdk_cli/tests/test_testnet.py @@ -17,7 +17,10 @@ def test_override_config(): assert config.metashard.consensus_size == 1 assert config.networking.port_proxy == 7950 assert config.software.mx_chain_go.resolution == SoftwareResolution.Remote - assert config.software.mx_chain_go.archive_url == "https://github.com/multiversx/mx-chain-go/archive/refs/heads/master.zip" + assert ( + config.software.mx_chain_go.archive_url + == "https://github.com/multiversx/mx-chain-go/archive/refs/heads/master.zip" + ) # Now partly override the config config_patch: Dict[str, Any] = dict() @@ -32,9 +35,7 @@ def test_override_config(): "port_proxy": 7951, } config_patch["software"] = { - "mx_chain_go": { - "archive_url": "https://github.com/multiversx/mx-chain-go/archive/refs/tags/v1.5.1.zip" - } + "mx_chain_go": {"archive_url": "https://github.com/multiversx/mx-chain-go/archive/refs/tags/v1.5.1.zip"} } config.override(config_patch) @@ -45,4 +46,7 @@ def test_override_config(): assert config.metashard.consensus_size == 2 assert config.networking.port_proxy == 7951 assert config.software.mx_chain_go.resolution == SoftwareResolution.Remote - assert config.software.mx_chain_go.archive_url == "https://github.com/multiversx/mx-chain-go/archive/refs/tags/v1.5.1.zip" + assert ( + config.software.mx_chain_go.archive_url + == "https://github.com/multiversx/mx-chain-go/archive/refs/tags/v1.5.1.zip" + ) diff --git a/multiversx_sdk_cli/tests/test_validators_core.py b/multiversx_sdk_cli/tests/test_validators_core.py index 0066b345..b382e2bb 100644 --- a/multiversx_sdk_cli/tests/test_validators_core.py +++ b/multiversx_sdk_cli/tests/test_validators_core.py @@ -2,8 +2,7 @@ from multiversx_sdk import Address -from multiversx_sdk_cli.validators.core import \ - prepare_transaction_data_for_stake +from multiversx_sdk_cli.validators.core import prepare_transaction_data_for_stake TESTDATA_FOLDER = Path(__file__).parent.joinpath("testdata") @@ -11,7 +10,12 @@ def test_prepare_transaction_data_for_stake(): node_operator_address = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") validators_file_path = TESTDATA_FOLDER / "validators.json" - data, gas_limit = prepare_transaction_data_for_stake(node_operator_address, validators_file_path, node_operator_address) + data, gas_limit = prepare_transaction_data_for_stake( + node_operator_address, validators_file_path, node_operator_address + ) - assert data == "stake@03@E7BEAA95B3877F47348DF4DD1CB578A4F7CABF7A20BFEEFE5CDD263878FF132B765E04FEF6F40C93512B666C47ED7719B8902F6C922C04247989B7137E837CC81A62E54712471C97A2DDAB75AA9C2F58F813ED4C0FA722BDE0AB718BFF382208@604882237A9845F508AD03877B5AAB90569683EEB51FAFCBBEB87440BA359992B3C0B837A8757C25BE18132549404F88@78689FD4B1E2E434D567FE01E61598A42717D83124308266BD09CCC15D2339DD318C019914B86AC29ADBAE5DD8A02D0307425E9BD85A296E94943708C72F8C670F0B7C50A890A5719088DBD9F1D062CAD9ACFFA06DF834106EEBE1A4257EF00D@EC54A009695AF56C3585EF623387B67B6DF1974B0B3C9138EB64BDE6EB33978AE9851112B20C99BF63588E8E949E4388@7188B234A8BF834F2E6258012AA09A2AB93178FFAB9C789480275F61FE02CD1B9A58DDC63B79A73ABEA9E2B7AC5CAC0B0D4324EFF50ACA2F0EC946B9AE6797511FA3CE461B57E77129CBA8AB3B51147695D4CE889CBE67905F6586B4E4F22491@C6C637DE17DB5F89A2FA1D1D935CB60C0E5E8958D3BFC47F903F774DD97398C8FE22093E113865EE98C3AFDD1DE62694@0139472EFF6886771A982F3083DA5D421F24C29181E63888228DC81CA60D69E1" + assert ( + data + == "stake@03@E7BEAA95B3877F47348DF4DD1CB578A4F7CABF7A20BFEEFE5CDD263878FF132B765E04FEF6F40C93512B666C47ED7719B8902F6C922C04247989B7137E837CC81A62E54712471C97A2DDAB75AA9C2F58F813ED4C0FA722BDE0AB718BFF382208@604882237A9845F508AD03877B5AAB90569683EEB51FAFCBBEB87440BA359992B3C0B837A8757C25BE18132549404F88@78689FD4B1E2E434D567FE01E61598A42717D83124308266BD09CCC15D2339DD318C019914B86AC29ADBAE5DD8A02D0307425E9BD85A296E94943708C72F8C670F0B7C50A890A5719088DBD9F1D062CAD9ACFFA06DF834106EEBE1A4257EF00D@EC54A009695AF56C3585EF623387B67B6DF1974B0B3C9138EB64BDE6EB33978AE9851112B20C99BF63588E8E949E4388@7188B234A8BF834F2E6258012AA09A2AB93178FFAB9C789480275F61FE02CD1B9A58DDC63B79A73ABEA9E2B7AC5CAC0B0D4324EFF50ACA2F0EC946B9AE6797511FA3CE461B57E77129CBA8AB3B51147695D4CE889CBE67905F6586B4E4F22491@C6C637DE17DB5F89A2FA1D1D935CB60C0E5E8958D3BFC47F903F774DD97398C8FE22093E113865EE98C3AFDD1DE62694@0139472EFF6886771A982F3083DA5D421F24C29181E63888228DC81CA60D69E1" + ) assert gas_limit == 16464500 diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 4bcc85f8..2d9deb71 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -4,29 +4,36 @@ import time from typing import Any, Dict, List, Protocol, TextIO, Union -from multiversx_sdk import (Address, Token, TokenComputer, TokenTransfer, - Transaction, - TransactionsFactoryConfig, - TransferTransactionsFactory, TransactionComputer, TransactionOnNetwork) +from multiversx_sdk import ( + Address, + Token, + TokenComputer, + TokenTransfer, + Transaction, + TransactionComputer, + TransactionOnNetwork, + TransactionsFactoryConfig, + TransferTransactionsFactory, +) from multiversx_sdk_cli import errors from multiversx_sdk_cli.accounts import Account, AccountBase, LedgerAccount -from multiversx_sdk_cli.cli_password import (load_guardian_password, - load_password) +from multiversx_sdk_cli.cli_password import load_guardian_password, load_password from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.errors import IncorrectWalletError, NoWalletProvided -from multiversx_sdk_cli.interfaces import ITransaction from multiversx_sdk_cli.ledger.ledger_functions import do_get_ledger_address logger = logging.getLogger("transactions") +# fmt: off class INetworkProvider(Protocol): def send_transaction(self, transaction: Transaction) -> bytes: ... def get_transaction(self, transaction_hash: Union[bytes, str]) -> TransactionOnNetwork: ... +# fmt: on def do_prepare_transaction(args: Any) -> Transaction: @@ -46,7 +53,7 @@ def do_prepare_transaction(args: Any) -> Transaction: receiver=receiver, native_amount=native_amount, token_transfers=transfers, - data=str(args.data).encode() + data=str(args.data).encode(), ) else: # this is for transactions with no token transfers(egld/esdt); useful for setting the data field @@ -55,7 +62,7 @@ def do_prepare_transaction(args: Any) -> Transaction: receiver=receiver, data=str(args.data).encode(), gas_limit=int(args.gas_limit), - chain_id=args.chain + chain_id=args.chain, ) tx.gas_limit = int(args.gas_limit) @@ -108,7 +115,10 @@ def do_prepare_transaction(args: Any) -> Transaction: def load_sender_account_from_args(args: Any) -> Account: account = Account() if args.ledger: - account = LedgerAccount(account_index=args.ledger_account_index, address_index=args.ledger_address_index) + account = LedgerAccount( + account_index=args.ledger_account_index, + address_index=args.ledger_address_index, + ) if args.pem: account = Account(pem_file=args.pem, pem_index=args.pem_index) elif args.keyfile: @@ -120,7 +130,10 @@ def load_sender_account_from_args(args: Any) -> Account: def load_relayer_account_from_args(args: Any) -> Account: if args.relayer_ledger: - account = LedgerAccount(account_index=args.relayer_ledger_account_index, address_index=args.relayer_ledger_address_index) + account = LedgerAccount( + account_index=args.relayer_ledger_account_index, + address_index=args.relayer_ledger_address_index, + ) if args.relayer_pem: account = Account(pem_file=args.relayer_pem, pem_index=args.relayer_pem_index) elif args.relayer_keyfile: @@ -165,7 +178,10 @@ def get_guardian_account_from_args(args: Any): password = load_guardian_password(args) account = Account(key_file=args.guardian_keyfile, password=password) elif args.guardian_ledger: - address = do_get_ledger_address(account_index=args.guardian_ledger_account_index, address_index=args.guardian_ledger_address_index) + address = do_get_ledger_address( + account_index=args.guardian_ledger_account_index, + address_index=args.guardian_ledger_address_index, + ) account = Account(address=Address.new_from_bech32(address)) else: raise errors.NoWalletProvided() @@ -181,7 +197,9 @@ def send_and_wait_for_result(transaction: Transaction, proxy: INetworkProvider, return txOnNetwork -def _send_transaction_and_wait_for_result(proxy: INetworkProvider, payload: Transaction, num_seconds_timeout: int = 100) -> TransactionOnNetwork: +def _send_transaction_and_wait_for_result( + proxy: INetworkProvider, payload: Transaction, num_seconds_timeout: int = 100 +) -> TransactionOnNetwork: AWAIT_TRANSACTION_PERIOD = 5 tx_hash = proxy.send_transaction(payload) @@ -233,7 +251,7 @@ def tx_to_dictionary_as_inner_for_relayed_V1(tx: Transaction) -> Dict[str, Any]: def _dict_to_json(dictionary: Dict[str, Any]) -> bytes: - serialized = json.dumps(dictionary, separators=(',', ':')).encode("utf8") + serialized = json.dumps(dictionary, separators=(",", ":")).encode("utf8") return serialized diff --git a/multiversx_sdk_cli/utils.py b/multiversx_sdk_cli/utils.py index a61e41fa..11c5ef36 100644 --- a/multiversx_sdk_cli/utils.py +++ b/multiversx_sdk_cli/utils.py @@ -8,8 +8,7 @@ import tarfile import zipfile from pathlib import Path -from typing import (Any, Dict, List, Optional, Protocol, Union, - runtime_checkable) +from typing import Any, Dict, List, Optional, Protocol, Union, runtime_checkable import toml @@ -88,7 +87,7 @@ def read_lines(file: Path) -> List[str]: def read_binary_file(path: Path) -> bytes: try: - with open(path, 'rb') as binary_file: + with open(path, "rb") as binary_file: return binary_file.read() except Exception as err: raise errors.BadFile(str(path), err) from None @@ -96,7 +95,7 @@ def read_binary_file(path: Path) -> bytes: def read_text_file(path: Path) -> str: try: - with open(path, 'r') as text_file: + with open(path, "r") as text_file: return text_file.read() except Exception as err: raise errors.BadFile(str(path), err) from None @@ -200,10 +199,10 @@ def str_int_to_hex_str(number_str: str) -> str: def parse_keys(bls_public_keys: str): - keys = bls_public_keys.split(',') - parsed_keys = '' + keys = bls_public_keys.split(",") + parsed_keys = "" for key in keys: - parsed_keys += '@' + key + parsed_keys += "@" + key return parsed_keys, len(keys) diff --git a/multiversx_sdk_cli/validators/__init__.py b/multiversx_sdk_cli/validators/__init__.py index 74a11e54..1c7ba1f4 100644 --- a/multiversx_sdk_cli/validators/__init__.py +++ b/multiversx_sdk_cli/validators/__init__.py @@ -1,23 +1,31 @@ from multiversx_sdk_cli.validators.core import ( - prepare_args_for_change_reward_address, prepare_args_for_claim, + prepare_args_for_change_reward_address, + prepare_args_for_claim, prepare_args_for_clean_registered_data, - prepare_args_for_restake_unstaked_nodes, prepare_args_for_stake, - prepare_args_for_unbond, prepare_args_for_unbond_nodes, - prepare_args_for_unbond_tokens, prepare_args_for_unjail, - prepare_args_for_unstake, prepare_args_for_unstake_nodes, - prepare_args_for_unstake_tokens) + prepare_args_for_restake_unstaked_nodes, + prepare_args_for_stake, + prepare_args_for_unbond, + prepare_args_for_unbond_nodes, + prepare_args_for_unbond_tokens, + prepare_args_for_unjail, + prepare_args_for_unstake, + prepare_args_for_unstake_nodes, + prepare_args_for_unstake_tokens, +) from multiversx_sdk_cli.validators.validators_file import ValidatorsFile -__all__ = ["prepare_args_for_stake", - "prepare_args_for_unstake", - "prepare_args_for_unbond", - "prepare_args_for_unjail", - "prepare_args_for_change_reward_address", - "prepare_args_for_claim", - "prepare_args_for_unstake_nodes", - "prepare_args_for_unstake_tokens", - "prepare_args_for_unbond_nodes", - "prepare_args_for_unbond_tokens", - "prepare_args_for_clean_registered_data", - "prepare_args_for_restake_unstaked_nodes", - "ValidatorsFile"] +__all__ = [ + "prepare_args_for_stake", + "prepare_args_for_unstake", + "prepare_args_for_unbond", + "prepare_args_for_unjail", + "prepare_args_for_change_reward_address", + "prepare_args_for_claim", + "prepare_args_for_unstake_nodes", + "prepare_args_for_unstake_tokens", + "prepare_args_for_unbond_nodes", + "prepare_args_for_unbond_tokens", + "prepare_args_for_clean_registered_data", + "prepare_args_for_restake_unstaked_nodes", + "ValidatorsFile", +] diff --git a/multiversx_sdk_cli/validators/core.py b/multiversx_sdk_cli/validators/core.py index f71d64ee..74269e7b 100644 --- a/multiversx_sdk_cli/validators/core.py +++ b/multiversx_sdk_cli/validators/core.py @@ -7,8 +7,12 @@ from multiversx_sdk_cli import utils from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.cli_password import load_password -from multiversx_sdk_cli.config import (GAS_PER_DATA_BYTE, MIN_GAS_LIMIT, - MetaChainSystemSCsCost, get_address_hrp) +from multiversx_sdk_cli.config import ( + GAS_PER_DATA_BYTE, + MIN_GAS_LIMIT, + MetaChainSystemSCsCost, + get_address_hrp, +) from multiversx_sdk_cli.contracts import prepare_execute_transaction_data from multiversx_sdk_cli.errors import BadUsage from multiversx_sdk_cli.validators.validators_file import ValidatorsFile @@ -42,7 +46,11 @@ def prepare_args_for_stake(args: Any): args.gas_limit = gas_limit -def prepare_transaction_data_for_stake(node_operator_address: Address, validators_file_path: Path, reward_address: Union[Address, None]) -> Tuple[str, int]: +def prepare_transaction_data_for_stake( + node_operator_address: Address, + validators_file_path: Path, + reward_address: Union[Address, None], +) -> Tuple[str, int]: validators_file = ValidatorsFile(validators_file_path) num_of_nodes = validators_file.get_num_of_nodes() validators_list = validators_file.get_validators_list() @@ -73,7 +81,7 @@ def prepare_transaction_data_for_stake(node_operator_address: Address, validator def prepare_args_for_top_up(args: Any): - args.data = 'stake' + args.data = "stake" args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -82,7 +90,7 @@ def prepare_args_for_top_up(args: Any): def prepare_args_for_unstake(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = 'unStake' + parsed_keys + args.data = "unStake" + parsed_keys args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -91,7 +99,7 @@ def prepare_args_for_unstake(args: Any): def prepare_args_for_unbond(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = 'unBond' + parsed_keys + args.data = "unBond" + parsed_keys args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -100,7 +108,7 @@ def prepare_args_for_unbond(args: Any): def prepare_args_for_unjail(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = 'unJail' + parsed_keys + args.data = "unJail" + parsed_keys args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -109,7 +117,7 @@ def prepare_args_for_unjail(args: Any): def prepare_args_for_change_reward_address(args: Any): reward_address = Address.new_from_bech32(args.reward_address) - args.data = 'changeRewardAddress@' + reward_address.hex() + args.data = "changeRewardAddress@" + reward_address.hex() args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -117,7 +125,7 @@ def prepare_args_for_change_reward_address(args: Any): def prepare_args_for_claim(args: Any): - args.data = 'claim' + args.data = "claim" args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -126,7 +134,7 @@ def prepare_args_for_claim(args: Any): def prepare_args_for_unstake_nodes(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = 'unStakeNodes' + parsed_keys + args.data = "unStakeNodes" + parsed_keys args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -134,8 +142,8 @@ def prepare_args_for_unstake_nodes(args: Any): def prepare_args_for_unstake_tokens(args: Any): - args.data = 'unStakeTokens' - args.data += '@' + utils.str_int_to_hex_str(str(args.unstake_value)) + args.data = "unStakeTokens" + args.data += "@" + utils.str_int_to_hex_str(str(args.unstake_value)) args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -144,7 +152,7 @@ def prepare_args_for_unstake_tokens(args: Any): def prepare_args_for_unbond_nodes(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = 'unBondNodes' + parsed_keys + args.data = "unBondNodes" + parsed_keys args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -152,8 +160,8 @@ def prepare_args_for_unbond_nodes(args: Any): def prepare_args_for_unbond_tokens(args: Any): - args.data = 'unBondTokens' - args.data += '@' + utils.str_int_to_hex_str(str(args.unbond_value)) + args.data = "unBondTokens" + args.data += "@" + utils.str_int_to_hex_str(str(args.unbond_value)) args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -161,7 +169,7 @@ def prepare_args_for_unbond_tokens(args: Any): def prepare_args_for_clean_registered_data(args: Any): - args.data = 'cleanRegisteredData' + args.data = "cleanRegisteredData" args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: @@ -170,7 +178,7 @@ def prepare_args_for_clean_registered_data(args: Any): def prepare_args_for_restake_unstaked_nodes(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = 'reStakeUnStakedNodes' + parsed_keys + args.data = "reStakeUnStakedNodes" + parsed_keys args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: diff --git a/multiversx_sdk_cli/validators/validators_file.py b/multiversx_sdk_cli/validators/validators_file.py index ab10535a..2543180a 100644 --- a/multiversx_sdk_cli/validators/validators_file.py +++ b/multiversx_sdk_cli/validators/validators_file.py @@ -40,7 +40,9 @@ def load_public_keys(self) -> List[ValidatorPublicKey]: def _load_validator_pem(self, validator: Dict[str, str]) -> ValidatorPEM: # Get path of "pemFile", make it absolute validator_pem = Path(validator.get("pemFile", "")).expanduser() - validator_pem = validator_pem if validator_pem.is_absolute() else self.validators_file_path.parent / validator_pem + validator_pem = ( + validator_pem if validator_pem.is_absolute() else self.validators_file_path.parent / validator_pem + ) return ValidatorPEM.from_file(validator_pem) diff --git a/multiversx_sdk_cli/workstation.py b/multiversx_sdk_cli/workstation.py index af1b5142..26c23584 100644 --- a/multiversx_sdk_cli/workstation.py +++ b/multiversx_sdk_cli/workstation.py @@ -30,7 +30,7 @@ def get_platform(): "darwin": "osx", "win32": "windows", "cygwin": "windows", - "msys": "windows" + "msys": "windows", } platform = platforms.get(sys.platform) From d00255a2cb3d49552b967704026f51c9f4731bbd Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 29 Jan 2025 13:10:44 +0200 Subject: [PATCH 03/84] add mypy as a dev dependency --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index e4368771..4b9d76b6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,3 +4,4 @@ autopep8 pytest-mock pre-commit black +mypy From adfa02760658aba83d4616312dd55ef2e4e34330 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 29 Jan 2025 14:48:17 +0200 Subject: [PATCH 04/84] drop support for relayed transactions v1 --- multiversx_sdk_cli/cli_contracts.py | 2 +- multiversx_sdk_cli/cli_delegation.py | 2 +- multiversx_sdk_cli/cli_dns.py | 2 +- multiversx_sdk_cli/cli_shared.py | 13 +---- multiversx_sdk_cli/cli_transactions.py | 10 +--- multiversx_sdk_cli/cli_validators.py | 2 +- multiversx_sdk_cli/dns.py | 9 +--- multiversx_sdk_cli/tests/test_cli_dns.py | 6 ++- .../tests/test_cli_transactions.py | 36 -------------- multiversx_sdk_cli/transactions.py | 48 +------------------ 10 files changed, 14 insertions(+), 116 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index c663d41f..1f653840 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -202,7 +202,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: default=100, help="max num of seconds to wait for result" " - only valid if --wait-result is set", ) - cli_shared.add_broadcast_args(sub, relay=True) + cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) sub.set_defaults(func=call) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 2e0a35f1..f5d409f7 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -356,7 +356,7 @@ def _add_common_arguments(args: List[str], sub: Any): cli_shared.add_proxy_arg(sub) cli_shared.add_wallet_args(args, sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False, with_estimate_gas=True) - cli_shared.add_broadcast_args(sub, relay=False) + cli_shared.add_broadcast_args(sub) cli_shared.add_outfile_arg(sub, what="signed transaction, hash") cli_shared.add_guardian_wallet_args(args, sub) diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index d26c1dc8..e4be41c0 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -30,7 +30,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: "Send a register transaction to the appropriate DNS contract from given user and with given name", ) cli_shared.add_outfile_arg(sub) - cli_shared.add_broadcast_args(sub, relay=True) + cli_shared.add_broadcast_args(sub) cli_shared.add_wallet_args(args, sub) cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index b6d0b2ab..443b3956 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -397,7 +397,7 @@ def prepare_chain_id_in_args(args: Any): args.chain = proxy.get_network_config().chain_id -def add_broadcast_args(sub: Any, simulate: bool = True, relay: bool = False): +def add_broadcast_args(sub: Any, simulate: bool = True): sub.add_argument( "--send", action="store_true", @@ -412,20 +412,9 @@ def add_broadcast_args(sub: Any, simulate: bool = True, relay: bool = False): default=False, help="whether to simulate the transaction (default: %(default)s)", ) - if relay: - sub.add_argument( - "--relay", - action="store_true", - default=False, - help="whether to relay the transaction (default: %(default)s)", - ) def check_broadcast_args(args: Any): - if hasattr(args, "relay") and args.relay and args.send: - raise errors.BadUsage( - "Cannot directly send a relayed transaction. Use 'mxpy tx new --relay' first, then 'mxpy tx send --data-file'" - ) if args.send and args.simulate: raise errors.BadUsage("Cannot both 'simulate' and 'send' a transaction") diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 692e3925..22e6fb04 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -10,7 +10,6 @@ from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.errors import IncorrectWalletError, NoWalletProvided from multiversx_sdk_cli.transactions import ( - compute_relayed_v1_data, do_prepare_transaction, load_transaction_from_file, ) @@ -31,7 +30,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: _add_common_arguments(args, sub) cli_shared.add_token_transfers_args(sub) cli_shared.add_outfile_arg(sub, what="signed transaction, hash") - cli_shared.add_broadcast_args(sub, relay=True) + cli_shared.add_broadcast_args(sub) cli_shared.add_proxy_arg(sub) cli_shared.add_guardian_wallet_args(args, sub) sub.add_argument( @@ -84,7 +83,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_wallet_args(args, sub) cli_shared.add_infile_arg(sub, what="a previously saved transaction") cli_shared.add_outfile_arg(sub, what="the signed transaction") - cli_shared.add_broadcast_args(sub, relay=True) + cli_shared.add_broadcast_args(sub) cli_shared.add_proxy_arg(sub) cli_shared.add_guardian_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -126,11 +125,6 @@ def create_transaction(args: Any): tx = do_prepare_transaction(args) - if hasattr(args, "relay") and args.relay: - logger.warning("RelayedV1 transactions are deprecated. Please use RelayedV3 instead.") - args.outfile.write(compute_relayed_v1_data(tx)) - return - cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index a7e4e016..0c23e73a 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -121,7 +121,7 @@ def _add_common_arguments(args: List[str], sub: Any): cli_shared.add_proxy_arg(sub) cli_shared.add_wallet_args(args, sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False, with_estimate_gas=True) - cli_shared.add_broadcast_args(sub, relay=False) + cli_shared.add_broadcast_args(sub) cli_shared.add_outfile_arg(sub, what="signed transaction, hash") cli_shared.add_guardian_wallet_args(args, sub) diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 6f1df414..8e277e11 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -15,10 +15,7 @@ from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX from multiversx_sdk_cli.contracts import SmartContract -from multiversx_sdk_cli.transactions import ( - compute_relayed_v1_data, - do_prepare_transaction, -) +from multiversx_sdk_cli.transactions import do_prepare_transaction MaxNumShards = 256 ShardIdentiferLen = 2 @@ -87,10 +84,6 @@ def register(args: Any): tx = do_prepare_transaction(args) - if hasattr(args, "relay") and args.relay: - args.outfile.write(compute_relayed_v1_data(tx)) - return - cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/tests/test_cli_dns.py b/multiversx_sdk_cli/tests/test_cli_dns.py index e038bb31..21da2b60 100644 --- a/multiversx_sdk_cli/tests/test_cli_dns.py +++ b/multiversx_sdk_cli/tests/test_cli_dns.py @@ -7,6 +7,7 @@ def test_prepare_relayed_dns_register_transaction(): alice = testdata_path / "alice.pem" + user = testdata_path / "testUser.pem" return_code = main( [ @@ -22,7 +23,10 @@ def test_prepare_relayed_dns_register_transaction(): "15000000", "--chain", "T", - "--relay", + "--relayer", + "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--relayer-pem", + str(user), ] ) diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index 1224e955..b9c3cea2 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -8,42 +8,6 @@ testdata_out = Path(__file__).parent / "testdata-out" -def test_relayed_v1_transaction(capsys: Any): - multi_user_pem = testdata_path / "multiple_addresses.pem" - address_index = 1 - - return_code = main( - [ - "tx", - "new", - "--pem", - str(multi_user_pem), - "--pem-index", - str(address_index), - "--receiver", - "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u", - "--nonce", - "198", - "--gas-limit", - "60000000", - "--data", - "getContractConfig", - "--version", - "1", - "--chain", - "T", - "--relay", - ] - ) - assert return_code == 0 - - relayed_tx = _read_stdout(capsys) - assert ( - relayed_tx - == "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414141415141414141414141414141414141414141414141414141414141432f2f383d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225a3256305132397564484a68593352446232356d6157633d222c227369676e6174757265223a2239682b6e6742584f5536776674315464437368534d4b3454446a5a32794f74686336564c576e3478724d5a706248427738677a6c6659596d362b766b505258303764634a562b4745635462616a7049692b5a5a5942773d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a317d" - ) - - def test_create_tx_and_sign_by_hash(capsys: Any): return_code = main( [ diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 2d9deb71..e23206b1 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -1,8 +1,7 @@ -import base64 import json import logging import time -from typing import Any, Dict, List, Protocol, TextIO, Union +from typing import Any, List, Protocol, TextIO, Union from multiversx_sdk import ( Address, @@ -217,51 +216,6 @@ def _send_transaction_and_wait_for_result( raise errors.KnownError("Took too long to get transaction.") -def tx_to_dictionary_as_inner_for_relayed_V1(tx: Transaction) -> Dict[str, Any]: - dictionary: Dict[str, Any] = {} - - dictionary["nonce"] = tx.nonce - dictionary["sender"] = base64.b64encode(tx.sender.get_public_key()).decode() - dictionary["receiver"] = base64.b64encode(tx.receiver.get_public_key()).decode() - dictionary["value"] = tx.value - dictionary["gasPrice"] = tx.gas_price - dictionary["gasLimit"] = tx.gas_limit - dictionary["data"] = base64.b64encode(tx.data).decode() - dictionary["signature"] = base64.b64encode(tx.signature).decode() - dictionary["chainID"] = base64.b64encode(tx.chain_id.encode()).decode() - dictionary["version"] = tx.version - - if tx.options: - dictionary["options"] = tx.options - - if tx.guardian: - guardian = tx.guardian.to_hex() - dictionary["guardian"] = base64.b64encode(bytes.fromhex(guardian)).decode() - - if tx.guardian_signature: - dictionary["guardianSignature"] = base64.b64encode(tx.guardian_signature).decode() - - if tx.sender_username: - dictionary["sndUserName"] = base64.b64encode(tx.sender_username.encode()).decode() - - if tx.receiver_username: - dictionary["rcvUserName"] = base64.b64encode(tx.receiver_username.encode()).decode() - - return dictionary - - -def _dict_to_json(dictionary: Dict[str, Any]) -> bytes: - serialized = json.dumps(dictionary, separators=(",", ":")).encode("utf8") - return serialized - - -def compute_relayed_v1_data(tx: Transaction) -> str: - inner_dictionary = tx_to_dictionary_as_inner_for_relayed_V1(tx) - serialized = _dict_to_json(inner_dictionary) - serialized_hex = serialized.hex() - return f"relayedTx@{serialized_hex}" - - def load_transaction_from_file(f: TextIO) -> Transaction: data_json: bytes = f.read().encode() transaction_dictionary = json.loads(data_json).get("tx") or json.loads(data_json).get("emittedTransaction") From 9c49436762383bc8a38f77ff78ef8de7eebff8cf Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 29 Jan 2025 15:52:59 +0200 Subject: [PATCH 05/84] drop contract report --- multiversx_sdk_cli/cli_contracts.py | 50 -------- multiversx_sdk_cli/projects/__init__.py | 2 - .../projects/report/__init__.py | 3 - .../projects/report/data/__init__.py | 0 .../projects/report/data/common.py | 61 ---------- .../projects/report/data/extracted_feature.py | 108 ----------------- .../projects/report/data/folder_report.py | 54 --------- .../projects/report/data/project_report.py | 70 ----------- .../projects/report/data/report.py | 110 ------------------ .../projects/report/data/wasm_report.py | 57 --------- .../projects/report/do_report.py | 82 ------------- .../projects/report/features/__init__.py | 0 .../projects/report/features/features.py | 15 --- .../projects/report/features/report_option.py | 29 ----- .../projects/report/features/size.py | 17 --- .../report/features/twiggy_paths_check.py | 71 ----------- .../projects/report/format/__init__.py | 0 .../projects/report/format/change_type.py | 37 ------ .../projects/report/format/format_options.py | 3 - .../projects/report/report_creator.py | 97 --------------- 20 files changed, 866 deletions(-) delete mode 100644 multiversx_sdk_cli/projects/report/__init__.py delete mode 100644 multiversx_sdk_cli/projects/report/data/__init__.py delete mode 100644 multiversx_sdk_cli/projects/report/data/common.py delete mode 100644 multiversx_sdk_cli/projects/report/data/extracted_feature.py delete mode 100644 multiversx_sdk_cli/projects/report/data/folder_report.py delete mode 100644 multiversx_sdk_cli/projects/report/data/project_report.py delete mode 100644 multiversx_sdk_cli/projects/report/data/report.py delete mode 100644 multiversx_sdk_cli/projects/report/data/wasm_report.py delete mode 100644 multiversx_sdk_cli/projects/report/do_report.py delete mode 100644 multiversx_sdk_cli/projects/report/features/__init__.py delete mode 100644 multiversx_sdk_cli/projects/report/features/features.py delete mode 100644 multiversx_sdk_cli/projects/report/features/report_option.py delete mode 100644 multiversx_sdk_cli/projects/report/features/size.py delete mode 100644 multiversx_sdk_cli/projects/report/features/twiggy_paths_check.py delete mode 100644 multiversx_sdk_cli/projects/report/format/__init__.py delete mode 100644 multiversx_sdk_cli/projects/report/format/change_type.py delete mode 100644 multiversx_sdk_cli/projects/report/format/format_options.py delete mode 100644 multiversx_sdk_cli/projects/report/report_creator.py diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 1f653840..4ce518bd 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -103,46 +103,6 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: ) sub.set_defaults(func=run_tests) - sub = cli_shared.add_command_subparser( - subparsers, - "contract", - "report", - "Print a detailed report of the smart contracts.", - ) - sub.add_argument( - "--skip-build", - action="store_true", - default=False, - help="skips the step of building of the wasm contracts", - ) - sub.add_argument( - "--skip-twiggy", - action="store_true", - default=False, - help="skips the steps of building the debug wasm files and running twiggy", - ) - sub.add_argument( - "--output-format", - type=str, - default="text-markdown", - choices=["github-markdown", "text-markdown", "json"], - help="report output format (default: %(default)s)", - ) - sub.add_argument( - "--output-file", - type=Path, - help="if specified, the output is written to a file, otherwise it's written to the standard output", - ) - sub.add_argument( - "--compare", - type=Path, - nargs="+", - metavar=("report-1.json", "report-2.json"), - help="create a comparison from two or more reports", - ) - _add_build_options_sc_meta(sub) - sub.set_defaults(func=do_report) - output_description = CLIOutputBuilder.describe( with_contract=True, with_transaction_on_network=True, with_simulation=True ) @@ -558,16 +518,6 @@ def build(args: Any): ) -def do_report(args: Any): - deprecation_message = "`mxpy contract report` is deprecated. Please use `sc-meta report` instead." - logger.warning(deprecation_message) - - args_dict = args.__dict__ - projects.do_report(args, args_dict) - - show_warning(deprecation_message) - - def run_tests(args: Any): check_if_rust_is_installed() projects.run_tests(args) diff --git a/multiversx_sdk_cli/projects/__init__.py b/multiversx_sdk_cli/projects/__init__.py index 791f43b6..d562dc41 100644 --- a/multiversx_sdk_cli/projects/__init__.py +++ b/multiversx_sdk_cli/projects/__init__.py @@ -6,13 +6,11 @@ ) from multiversx_sdk_cli.projects.project_base import Project from multiversx_sdk_cli.projects.project_rust import ProjectRust -from multiversx_sdk_cli.projects.report.do_report import do_report from multiversx_sdk_cli.projects.templates import Contract __all__ = [ "build_project", "clean_project", - "do_report", "run_tests", "load_project", "Project", diff --git a/multiversx_sdk_cli/projects/report/__init__.py b/multiversx_sdk_cli/projects/report/__init__.py deleted file mode 100644 index 54ebbb27..00000000 --- a/multiversx_sdk_cli/projects/report/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .do_report import do_report - -__all__ = ["do_report"] diff --git a/multiversx_sdk_cli/projects/report/data/__init__.py b/multiversx_sdk_cli/projects/report/data/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/multiversx_sdk_cli/projects/report/data/common.py b/multiversx_sdk_cli/projects/report/data/common.py deleted file mode 100644 index a306ce64..00000000 --- a/multiversx_sdk_cli/projects/report/data/common.py +++ /dev/null @@ -1,61 +0,0 @@ -import itertools -from collections import OrderedDict -from typing import Callable, List, Optional, TypeVar - - -def flatten_list_of_rows(list_of_rows: List[List[List[str]]]) -> List[List[str]]: - return list(itertools.chain(*list_of_rows)) - - -def merge_values(first: List[str], second: List[str]) -> List[str]: - return list(OrderedDict.fromkeys(first + second)) - - -T = TypeVar("T") -K = TypeVar("K") - - -def first_not_none(first: Optional[T], second: Optional[T]) -> T: - return next(item for item in [first, second] if item is not None) - - -def get_keys(items: List[T], key_getter: Callable[[T], K]) -> List[K]: - return [key_getter(item) for item in items] - - -def list_as_key_value_dict(items: List[T], key_getter: Callable[[T], K]) -> "OrderedDict[K, T]": - return OrderedDict(zip(get_keys(items, key_getter), items)) - - -def merge_values_by_key( - first: List[T], - second: List[T], - key_getter: Callable[[T], K], - merge: Callable[[Optional[T], Optional[T]], T], -) -> List[T]: - """ - Merge the values of two lists when the key matches. - Used in order to de-duplicate report entries depending on certain criteria, such as paths or feature names. - - >>> def merge_func(a, b): - ... if a == None: - ... return (b[0], b[1] + 100) - ... if b == None: - ... return (a[0], a[1] + 200) - ... return (a[0], a[1] + b[1]) - >>> first = [('one', 1), ('two', 2)] - >>> second = [('two', 3), ('three', 4)] - >>> key_getter = lambda item: item[0] - >>> merge_values_by_key(first, second, key_getter, merge_func) - [('one', 201), ('two', 5), ('three', 104)] - """ - first_as_dict = list_as_key_value_dict(first, key_getter) - second_as_dict = list_as_key_value_dict(second, key_getter) - union = OrderedDict.fromkeys(list(first_as_dict.keys()) + list(second_as_dict.keys())) - all_keys = union.keys() - result = [] - for key in all_keys: - first_value = first_as_dict.get(key) - second_value = second_as_dict.get(key) - result.append(merge(first_value, second_value)) - return result diff --git a/multiversx_sdk_cli/projects/report/data/extracted_feature.py b/multiversx_sdk_cli/projects/report/data/extracted_feature.py deleted file mode 100644 index 724f15ac..00000000 --- a/multiversx_sdk_cli/projects/report/data/extracted_feature.py +++ /dev/null @@ -1,108 +0,0 @@ -from typing import Any, List, Optional - -from multiversx_sdk_cli.projects.report.data.common import ( - first_not_none, - merge_values_by_key, -) -from multiversx_sdk_cli.projects.report.format.change_type import ChangeType -from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions - - -class ExtractedFeature: - def __init__(self, feature_name: str, results: List[str]) -> None: - self.feature_name = feature_name - self.results = results - - def to_json(self) -> Any: - return {"feature_name": self.feature_name, "results": self.results} - - @staticmethod - def from_json(json: Any) -> "ExtractedFeature": - return ExtractedFeature(json["feature_name"], json["results"]) - - def results_to_markdown(self, format_options: FormatOptions) -> str: - separator = " :arrow_right: " if format_options.github_flavor else " -> " - change_type = self._classify_changes() - display_results = _prepare_results_for_markdown(self.results) - if change_type == ChangeType.NONE: - return display_results[0] - else: - return separator.join(display_results) + " " + change_type.to_markdown(format_options) - - def _classify_changes(self) -> ChangeType: - """ - Used to classify if a change in a feature's results is good, bad etc. - Required because changes in values are not always intuitive: - - a higher value may be worse than a small one (eg. for file sizes) - - a 'Yes' may be worse than a 'No' - This information is used to draw small icons at the end of a cell containing a change. - """ - - all_results_are_identical = all(result == self.results[0] for result in self.results) - if all_results_are_identical: - return ChangeType.NONE - any_is_not_available = any(result == "N/A" for result in self.results) - if any_is_not_available: - return ChangeType.UNKNOWN - if self.feature_name == "size": - sizes = list(map(int, self.results)) - return _classify_list(sizes, reverse=True) - if self.feature_name == "has-allocator" or self.feature_name == "has-format": - presence_checks = list(map(lambda value: False if value == "False" else True, self.results)) - return _classify_list(presence_checks, reverse=True) - return ChangeType.UNKNOWN - - -def _prepare_results_for_markdown(results: List[str]) -> List[str]: - return list(map(_replace_bool_with_yes_no, results)) - - -def _replace_bool_with_yes_no(item: str) -> str: - if item == "True": - return "Yes" - if item == "False": - return "No" - return item - - -def _classify_list(items: List[Any], reverse: bool = False) -> ChangeType: - if reverse: - items.reverse() - if _is_strictly_better(items): - return ChangeType.GOOD - if _is_strictly_worse(items): - return ChangeType.BAD - return ChangeType.MIXED - - -def _is_strictly_better(items: List[Any]) -> bool: - return sorted(items) == items - - -def _is_strictly_worse(items: List[Any]) -> bool: - return sorted(items, reverse=True) == items - - -def merge_lists_of_extracted_features( - first: List[ExtractedFeature], second: List[ExtractedFeature] -) -> List[ExtractedFeature]: - return merge_values_by_key(first, second, _get_extracted_feature_key, _merge_two_extracted_features) - - -def _get_extracted_feature_key(extracted_feature: ExtractedFeature) -> str: - return extracted_feature.feature_name - - -def _merge_two_extracted_features( - first: Optional[ExtractedFeature], second: Optional[ExtractedFeature] -) -> ExtractedFeature: - any = first_not_none(first, second) - merged_results = _results_or_NA(first) + _results_or_NA(second) - return ExtractedFeature(any.feature_name, merged_results) - - -def _results_or_NA(extracted_feature: Optional[ExtractedFeature]) -> List[str]: - if extracted_feature is None: - return ["N/A"] - else: - return extracted_feature.results diff --git a/multiversx_sdk_cli/projects/report/data/folder_report.py b/multiversx_sdk_cli/projects/report/data/folder_report.py deleted file mode 100644 index a9d10984..00000000 --- a/multiversx_sdk_cli/projects/report/data/folder_report.py +++ /dev/null @@ -1,54 +0,0 @@ -from pathlib import Path -from typing import Any, List, Optional - -from multiversx_sdk_cli.projects.report.data.common import ( - first_not_none, - flatten_list_of_rows, - merge_values_by_key, -) -from multiversx_sdk_cli.projects.report.data.project_report import ( - ProjectReport, - merge_list_of_projects, -) -from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions - - -class FolderReport: - def __init__(self, root_path: Path, projects: List[ProjectReport]) -> None: - self.root_path = root_path - self.projects = projects - - def to_json(self) -> Any: - return {"root_path": str(self.root_path), "projects": self.projects} - - @staticmethod - def from_json(json: Any) -> "FolderReport": - projects = [ProjectReport.from_json(project) for project in json["projects"]] - return FolderReport(Path(json["root_path"]), projects) - - def get_markdown_rows(self, format_options: FormatOptions) -> List[List[str]]: - folder_row = [str(self.root_path)] - project_rows = flatten_list_of_rows([project.get_rows_markdown(format_options) for project in self.projects]) - return [folder_row] + project_rows - - -def merge_list_of_folder_reports(first: List[FolderReport], second: List[FolderReport]) -> List[FolderReport]: - return merge_values_by_key(first, second, _get_folder_report_root_path, _merge_two_folder_reports) - - -def _get_folder_report_root_path(item: FolderReport) -> Path: - return item.root_path - - -def _projects_or_default(folder_report: Optional[FolderReport]) -> List[ProjectReport]: - if folder_report is None: - return [] - return folder_report.projects - - -def _merge_two_folder_reports(first: Optional[FolderReport], second: Optional[FolderReport]) -> FolderReport: - any = first_not_none(first, second) - first_projects = _projects_or_default(first) - second_projects = _projects_or_default(second) - merged_projects = merge_list_of_projects(first_projects, second_projects) - return FolderReport(any.root_path, merged_projects) diff --git a/multiversx_sdk_cli/projects/report/data/project_report.py b/multiversx_sdk_cli/projects/report/data/project_report.py deleted file mode 100644 index 852efe18..00000000 --- a/multiversx_sdk_cli/projects/report/data/project_report.py +++ /dev/null @@ -1,70 +0,0 @@ -from pathlib import Path -from typing import Any, List, Optional - -from multiversx_sdk_cli.projects.report.data.common import ( - first_not_none, - merge_values_by_key, -) -from multiversx_sdk_cli.projects.report.data.wasm_report import ( - WasmReport, - merge_list_of_wasm_reports, -) -from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions - - -class ProjectReport: - def __init__(self, project_path: Path, wasm_reports: List[WasmReport]) -> None: - self.project_path = project_path - self.wasm_reports = wasm_reports - - def to_json(self) -> Any: - return { - "project_path": str(self.project_path), - "wasm_reports": self.wasm_reports, - } - - @staticmethod - def from_json(json: Any) -> "ProjectReport": - wasm_reports = [WasmReport.from_json(wasm_report) for wasm_report in json["wasm_reports"]] - return ProjectReport(Path(json["project_path"]), wasm_reports) - - def get_rows_markdown(self, format_options: FormatOptions) -> List[List[str]]: - wasm_count = len(self.wasm_reports) - if wasm_count == 0: - return [[f" - {str(self.project_path)} "]] - elif wasm_count == 1: - return [ - [f" - {str(self.project_path / self.wasm_reports[0].wasm_name)}"] - + self.wasm_reports[0].get_extracted_features_markdown(format_options) - ] - else: - project_path_row = [f" - {str(self.project_path)}"] - wasm_rows = [ - [f" - - {wasm.wasm_name}"] + wasm.get_extracted_features_markdown(format_options) - for wasm in self.wasm_reports - ] - return [project_path_row] + wasm_rows - - -def merge_list_of_projects(first: List[ProjectReport], second: List[ProjectReport]) -> List[ProjectReport]: - return merge_values_by_key(first, second, _get_project_report_path, _merge_two_project_reports) - - -def _get_project_report_path(project_report: ProjectReport) -> Path: - return project_report.project_path - - -def _wasm_reports_or_default( - project_report: Optional[ProjectReport], -) -> List[WasmReport]: - if project_report is None: - return [] - return project_report.wasm_reports - - -def _merge_two_project_reports(first: Optional[ProjectReport], second: Optional[ProjectReport]) -> ProjectReport: - any = first_not_none(first, second) - first_wasm_reports = _wasm_reports_or_default(first) - second_wasm_reports = _wasm_reports_or_default(second) - merged_wasm_reports = merge_list_of_wasm_reports(first_wasm_reports, second_wasm_reports) - return ProjectReport(any.project_path, merged_wasm_reports) diff --git a/multiversx_sdk_cli/projects/report/data/report.py b/multiversx_sdk_cli/projects/report/data/report.py deleted file mode 100644 index 87f5f5f6..00000000 --- a/multiversx_sdk_cli/projects/report/data/report.py +++ /dev/null @@ -1,110 +0,0 @@ -import functools -import json -from io import StringIO -from pathlib import Path -from typing import Any, List - -from multiversx_sdk_cli.projects.report.data.common import ( - flatten_list_of_rows, - merge_values, -) -from multiversx_sdk_cli.projects.report.data.folder_report import ( - FolderReport, - merge_list_of_folder_reports, -) -from multiversx_sdk_cli.projects.report.format.change_type import ChangeType -from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions - - -class Report: - def __init__(self, feature_names: List[str], folders: List[FolderReport]) -> None: - self.feature_names = feature_names - self.folders = folders - - def to_json(self) -> Any: - return {"features": self.feature_names, "folders": self.folders} - - @staticmethod - def from_json(json: Any) -> "Report": - folders = [FolderReport.from_json(folder_report) for folder_report in json["folders"]] - return Report(json["features"], folders) - - @staticmethod - def load_from_file(report_json_path: Path) -> "Report": - with open(report_json_path, "r") as report_file: - report_json = json.load(report_file) - return Report.from_json(report_json) - - def get_markdown_rows(self, format_options: FormatOptions) -> List[List[str]]: - rows = [folder_report.get_markdown_rows(format_options) for folder_report in self.folders] - return flatten_list_of_rows(rows) - - def to_markdown(self, format_options: FormatOptions) -> str: - text = StringIO() - - table_headers = ["Path"] + self.feature_names - _adjust_table_headers(table_headers, format_options) - _write_markdown_row(text, table_headers, format_options) - - ALIGN_LEFT = ":--" - ALIGN_RIGHT = "--:" - row_alignments = [ALIGN_LEFT] + len(self.feature_names) * [ALIGN_RIGHT] - _write_markdown_row(text, row_alignments, format_options) - - for row in self.get_markdown_rows(format_options): - _write_markdown_row(text, row, format_options) - - return text.getvalue() - - def to_json_string(self) -> str: - return json.dumps(self, indent=4, default=lambda obj: obj.to_json()) - - -# Adjusts the column widths in github tables - see: -# https://github.com/markedjs/marked/issues/266#issuecomment-616347986 -def _adjust_table_headers(table_headers: List[str], format_options: FormatOptions) -> None: - if not format_options.github_flavor: - return - NBSP = "\u00a0" - table_headers[0] = table_headers[0].ljust(60, NBSP) - table_headers[1] = table_headers[1].rjust(40, NBSP) - table_headers[2] = table_headers[2].rjust(30, NBSP) - table_headers[3] = table_headers[3].rjust(30, NBSP) - - -def merge_list_of_reports(reports: List[Report]) -> Report: - return functools.reduce(_merge_two_reports, reports) - - -def _merge_two_reports(first: Report, other: Report) -> Report: - feature_names = merge_values(first.feature_names, other.feature_names) - folders = merge_list_of_folder_reports(first.folders, other.folders) - return Report(feature_names, folders) - - -# Hack in order to keep the column alignment in a terminal -# as unicode characters are sometimes wider or narrower -def _justify_text_string(string: str, width: int) -> str: - if ChangeType.UNKNOWN._to_text_markdown() in string: - width += 1 - if ChangeType.GOOD._to_text_markdown() in string: - width -= 1 - if ChangeType.BAD._to_text_markdown() in string: - width -= 1 - return string.rjust(width) - - -def _format_row_markdown(row: List[str], format_options: FormatOptions) -> str: - row += [""] * (4 - len(row)) - if not format_options.github_flavor: - row[0] = row[0].ljust(100) - row[1] = _justify_text_string(row[1], 20) - row[2] = _justify_text_string(row[2], 20) - row[3] = _justify_text_string(row[3], 20) - merged_cells = " | ".join(row) - return f"| {merged_cells} |" - - -def _write_markdown_row(string: StringIO, row: List[str], format_options: FormatOptions): - string.write(_format_row_markdown(row, format_options)) - string.write("\n") diff --git a/multiversx_sdk_cli/projects/report/data/wasm_report.py b/multiversx_sdk_cli/projects/report/data/wasm_report.py deleted file mode 100644 index e3cc9dd7..00000000 --- a/multiversx_sdk_cli/projects/report/data/wasm_report.py +++ /dev/null @@ -1,57 +0,0 @@ -from typing import Any, List, Optional - -from multiversx_sdk_cli.projects.report.data.common import ( - first_not_none, - merge_values_by_key, -) -from multiversx_sdk_cli.projects.report.data.extracted_feature import ( - ExtractedFeature, - merge_lists_of_extracted_features, -) -from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions - - -class WasmReport: - def __init__(self, wasm_name: str, extracted_features: List[ExtractedFeature]) -> None: - self.wasm_name = wasm_name - self.extracted_features = extracted_features - - def to_json(self) -> Any: - return { - "wasm_name": self.wasm_name, - "extracted_features": self.extracted_features, - } - - @staticmethod - def from_json(json: Any) -> "WasmReport": - extracted_features = [ - ExtractedFeature.from_json(extracted_feature) for extracted_feature in json["extracted_features"] - ] - return WasmReport(json["wasm_name"], extracted_features) - - def get_extracted_features_markdown(self, format_options: FormatOptions) -> List[str]: - return [extracted_feature.results_to_markdown(format_options) for extracted_feature in self.extracted_features] - - -def merge_list_of_wasm_reports(first: List[WasmReport], second: List[WasmReport]) -> List[WasmReport]: - return merge_values_by_key(first, second, _get_wasm_report_key, merge_two_wasm_reports) - - -def _get_wasm_report_key(wasm_report: WasmReport) -> str: - return wasm_report.wasm_name - - -def _get_extracted_features_or_default( - wasm: Optional[WasmReport], -) -> List[ExtractedFeature]: - if wasm is None: - return [] - return wasm.extracted_features - - -def merge_two_wasm_reports(first: Optional[WasmReport], second: Optional[WasmReport]) -> WasmReport: - any = first_not_none(first, second) - first_extracted_features = _get_extracted_features_or_default(first) - second_extracted_features = _get_extracted_features_or_default(second) - merged_extracted_features = merge_lists_of_extracted_features(first_extracted_features, second_extracted_features) - return WasmReport(any.wasm_name, merged_extracted_features) diff --git a/multiversx_sdk_cli/projects/report/do_report.py b/multiversx_sdk_cli/projects/report/do_report.py deleted file mode 100644 index a0379331..00000000 --- a/multiversx_sdk_cli/projects/report/do_report.py +++ /dev/null @@ -1,82 +0,0 @@ -import copy -import logging -from pathlib import Path -from typing import Any, Dict, List - -from multiversx_sdk_cli import cli_shared, utils -from multiversx_sdk_cli.projects.core import get_project_paths_recursively -from multiversx_sdk_cli.projects.report.data.report import Report, merge_list_of_reports -from multiversx_sdk_cli.projects.report.features.features import ( - get_default_report_features, -) -from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions -from multiversx_sdk_cli.projects.report.report_creator import ReportCreator - -logger = logging.getLogger("report") - - -def do_report(args: Any, build_options: Any) -> None: - compare_report_paths = args.compare - if compare_report_paths is None: - _build_report(args, build_options) - else: - _compare_reports(args, compare_report_paths) - - -def _build_report(args: Any, build_options: Dict[str, Any]) -> None: - base_path = Path(args.path) - project_paths = get_project_paths_recursively(base_path) - options = get_default_report_features() - - args_copy = _prepare_args_for_build(args) - build_args = cli_shared.convert_args_object_to_args_list(args_copy) - - report_creator = ReportCreator( - options, - skip_build=args.skip_build, - skip_twiggy=args.skip_twiggy, - build_options=build_options, - build_args=build_args, - ) - report = report_creator.create_report(base_path, project_paths) - _finalize_report(report, args) - - -def _prepare_args_for_build(args: Any): - args_copy = copy.deepcopy(args) - - arguments: Dict[str, Any] = args_copy.__dict__ - arguments.pop("output_format", None) - arguments.pop("output_file", None) - - return args_copy - - -def _compare_reports(args: Any, merge_report_paths: List[Path]) -> None: - reports = [Report.load_from_file(report_path) for report_path in merge_report_paths] - final_report = merge_list_of_reports(reports) - _finalize_report(final_report, args) - - -def _finalize_report(report: Report, args: Any) -> None: - output = _get_report_output_string(report, args) - _store_output(output, args) - - -def _get_report_output_string(report: Report, args: Any) -> str: - output_format = args.output_format - if output_format == "github-markdown": - return report.to_markdown(FormatOptions(github_markdown=True)) - if output_format == "text-markdown": - return report.to_markdown(FormatOptions(github_markdown=False)) - elif output_format == "json": - return report.to_json_string() - raise Exception("Invalid output format") - - -def _store_output(output: str, args: Any) -> None: - output_file_path = args.output_file - if output_file_path is None: - print(output) - else: - utils.write_file(Path(output_file_path), output) diff --git a/multiversx_sdk_cli/projects/report/features/__init__.py b/multiversx_sdk_cli/projects/report/features/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/multiversx_sdk_cli/projects/report/features/features.py b/multiversx_sdk_cli/projects/report/features/features.py deleted file mode 100644 index 223115db..00000000 --- a/multiversx_sdk_cli/projects/report/features/features.py +++ /dev/null @@ -1,15 +0,0 @@ -from typing import List - -from multiversx_sdk_cli.projects.report.features.report_option import ReportFeature -from multiversx_sdk_cli.projects.report.features.size import Size -from multiversx_sdk_cli.projects.report.features.twiggy_paths_check import ( - TwiggyPathsCheck, -) - - -def get_default_report_features() -> List[ReportFeature]: - return [ - Size("size"), - TwiggyPathsCheck("has-allocator", pattern="wee_alloc::"), - TwiggyPathsCheck("has-format", pattern="core::fmt"), - ] diff --git a/multiversx_sdk_cli/projects/report/features/report_option.py b/multiversx_sdk_cli/projects/report/features/report_option.py deleted file mode 100644 index 55f5ac21..00000000 --- a/multiversx_sdk_cli/projects/report/features/report_option.py +++ /dev/null @@ -1,29 +0,0 @@ -from abc import abstractmethod -from pathlib import Path -from typing import Any, Optional - - -class ReportFeature: - """ - Base class for any feature in a report. - - A feature represents a column in a report. - The name argument will appear as the column header in a report. - The implementation of the extract method will determine the contents of each cell in said column. - """ - - def __init__(self, name: str) -> None: - self.name = name - - @abstractmethod - def extract(self, wasm_path: Path) -> str: - pass - - def requires_twiggy_paths(self) -> bool: - return False - - -def str_or_default(field: Optional[Any], default: str = "-") -> str: - if field is None: - return default - return str(field) diff --git a/multiversx_sdk_cli/projects/report/features/size.py b/multiversx_sdk_cli/projects/report/features/size.py deleted file mode 100644 index 2b162d22..00000000 --- a/multiversx_sdk_cli/projects/report/features/size.py +++ /dev/null @@ -1,17 +0,0 @@ -from pathlib import Path -from typing import Optional - -from .report_option import ReportFeature, str_or_default - - -class Size(ReportFeature): - def extract(self, wasm_path: Path): - size = _get_file_size(wasm_path) - return str_or_default(size) - - -def _get_file_size(file_path: Path) -> Optional[int]: - try: - return int(file_path.stat().st_size) - except FileNotFoundError: - return None diff --git a/multiversx_sdk_cli/projects/report/features/twiggy_paths_check.py b/multiversx_sdk_cli/projects/report/features/twiggy_paths_check.py deleted file mode 100644 index 22334727..00000000 --- a/multiversx_sdk_cli/projects/report/features/twiggy_paths_check.py +++ /dev/null @@ -1,71 +0,0 @@ -import logging -from pathlib import Path - -from multiversx_sdk_cli import dependencies, myprocess, utils -from multiversx_sdk_cli.errors import BadFile -from multiversx_sdk_cli.projects.report.features.report_option import ReportFeature - -logger = logging.getLogger("projects.report.options.twiggy_paths_check") - - -class TwiggyPathsCheck(ReportFeature): - def __init__(self, name: str, pattern: str) -> None: - super().__init__(name) - - self.pattern = pattern - - def extract(self, wasm_path: Path) -> str: - twiggy_paths_path = _get_twiggy_paths_path(wasm_path) - try: - text = utils.read_text_file(twiggy_paths_path) - return str(self.pattern in text) - except BadFile: - return "N/A" - - def requires_twiggy_paths(self): - return True - - -def run_twiggy_paths(wasm_path: Path) -> Path: - rust = dependencies.get_module_by_key("rust") - debug_wasm_path = _get_debug_wasm_path(wasm_path) - - twiggy_paths_args = ["twiggy", "paths", str(debug_wasm_path)] - output = myprocess.run_process( - twiggy_paths_args, - env=rust.get_env(), - cwd=debug_wasm_path.parent, - dump_to_stdout=False, - ) - - output_path = _get_twiggy_paths_path(wasm_path) - utils.write_file(output_path, output) - logger.info(f"Twiggy paths output path: {output_path}") - return output_path - - -def _replace_file_suffix(file_path: Path, suffix: str) -> Path: - new_name = file_path.stem + suffix - return file_path.with_name(new_name) - - -def _add_file_prefix(file_path: Path, prefix: str) -> Path: - new_name = prefix + file_path.name - return file_path.with_name(new_name) - - -def _get_debug_wasm_path(wasm_path: Path) -> Path: - """ - >>> _get_debug_wasm_path(Path('test/contract.wasm')) - PosixPath('test/contract-dbg.wasm') - """ - return _replace_file_suffix(wasm_path, "-dbg.wasm") - - -def _get_twiggy_paths_path(wasm_path: Path) -> Path: - """ - >>> _get_twiggy_paths_path(Path('test/contract.wasm')) - PosixPath('test/twiggy-paths-contract-dbg.txt') - """ - txt_file_path = _replace_file_suffix(wasm_path, "-dbg.txt") - return _add_file_prefix(txt_file_path, "twiggy-paths-") diff --git a/multiversx_sdk_cli/projects/report/format/__init__.py b/multiversx_sdk_cli/projects/report/format/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/multiversx_sdk_cli/projects/report/format/change_type.py b/multiversx_sdk_cli/projects/report/format/change_type.py deleted file mode 100644 index 5b036f23..00000000 --- a/multiversx_sdk_cli/projects/report/format/change_type.py +++ /dev/null @@ -1,37 +0,0 @@ -from enum import Enum, auto - -from multiversx_sdk_cli.projects.report.format.format_options import FormatOptions - - -class ChangeType(Enum): - UNKNOWN = auto() - NONE = auto() - GOOD = auto() - BAD = auto() - MIXED = auto() - - def to_markdown(self, format_options: FormatOptions) -> str: - if format_options.github_flavor: - return self._to_github_markdown() - else: - return self._to_text_markdown() - - def _to_github_markdown(self) -> str: - switch = { - ChangeType.UNKNOWN: ":warning:", - ChangeType.NONE: "", - ChangeType.GOOD: ":green_circle:", - ChangeType.BAD: ":red_circle:", - ChangeType.MIXED: ":yellow_circle:", - } - return switch[self] - - def _to_text_markdown(self) -> str: - switch = { - ChangeType.UNKNOWN: "\u26a0\ufe0f ", - ChangeType.NONE: "", - ChangeType.GOOD: "\U0001f34f", - ChangeType.BAD: "\u274c", - ChangeType.MIXED: "\U0001f536\ufe0f", - } - return switch[self] diff --git a/multiversx_sdk_cli/projects/report/format/format_options.py b/multiversx_sdk_cli/projects/report/format/format_options.py deleted file mode 100644 index ca2a068c..00000000 --- a/multiversx_sdk_cli/projects/report/format/format_options.py +++ /dev/null @@ -1,3 +0,0 @@ -class FormatOptions: - def __init__(self, github_markdown: bool) -> None: - self.github_flavor = github_markdown diff --git a/multiversx_sdk_cli/projects/report/report_creator.py b/multiversx_sdk_cli/projects/report/report_creator.py deleted file mode 100644 index 9b67a7bd..00000000 --- a/multiversx_sdk_cli/projects/report/report_creator.py +++ /dev/null @@ -1,97 +0,0 @@ -import itertools -import operator -from pathlib import Path -from typing import Any, Dict, Iterable, List, Tuple - -from multiversx_sdk_cli import guards -from multiversx_sdk_cli.projects.core import load_project -from multiversx_sdk_cli.projects.project_base import remove_suffix -from multiversx_sdk_cli.projects.project_rust import ProjectRust -from multiversx_sdk_cli.projects.report.data.extracted_feature import ExtractedFeature -from multiversx_sdk_cli.projects.report.data.folder_report import FolderReport -from multiversx_sdk_cli.projects.report.data.project_report import ProjectReport -from multiversx_sdk_cli.projects.report.data.report import Report -from multiversx_sdk_cli.projects.report.data.wasm_report import WasmReport -from multiversx_sdk_cli.projects.report.features.report_option import ReportFeature -from multiversx_sdk_cli.projects.report.features.twiggy_paths_check import ( - run_twiggy_paths, -) - - -class ReportCreator: - def __init__( - self, - options: List[ReportFeature], - skip_build: bool, - skip_twiggy: bool, - build_options: Dict[str, Any], - build_args: List[str], - ) -> None: - self.report_features = options - self.skip_build = skip_build - self.skip_twiggy = skip_twiggy - self.require_twiggy_paths = any( - report_feature.requires_twiggy_paths() for report_feature in self.report_features - ) - self.build_options = build_options - self.build_args = build_args - - def create_report(self, base_path: Path, project_paths: List[Path]) -> Report: - base_path = base_path.resolve() - guards.is_directory(base_path) - - folder_groups = [ - self._create_folder_report(base_path, parent_folder, iter) - for parent_folder, iter in _group_projects_by_folder(project_paths) - ] - - feature_names = [report_feature.name for report_feature in self.report_features] - return Report(feature_names, folder_groups) - - def _create_folder_report( - self, base_path: Path, parent_folder: Path, iter: Iterable[Tuple[Path, Path]] - ) -> FolderReport: - parent_folder = parent_folder.resolve() - project_reports = [self._create_project_report(parent_folder, project_path) for _, project_path in iter] - - root_path = parent_folder.relative_to(base_path.parent) - return FolderReport(root_path, project_reports) - - def _create_project_report(self, parent_path: Path, project_path: Path) -> ProjectReport: - project_path = project_path.resolve() - project = load_project(project_path) - - if not self.skip_build: - project.build(self.build_args, self.build_options) - - twiggy_requirements_met = False - should_build_twiggy = self.require_twiggy_paths and not self.skip_twiggy - if should_build_twiggy and isinstance(project, ProjectRust): - project.build_wasm_with_debug_symbols(self.build_options) - twiggy_requirements_met = True - - wasm_reports = [ - self._create_wasm_report(wasm_path, twiggy_requirements_met) for wasm_path in project.find_wasm_files() - ] - wasm_reports.sort(key=lambda report: remove_suffix(report.wasm_name, ".wasm")) - - return ProjectReport(project_path.relative_to(parent_path), wasm_reports) - - def _create_wasm_report(self, wasm_path: Path, twiggy_requirements_met: bool) -> WasmReport: - if twiggy_requirements_met: - run_twiggy_paths(wasm_path) - name = wasm_path.name - extracted_features = [_extract_feature(report_feature, wasm_path) for report_feature in self.report_features] - return WasmReport(name, extracted_features) - - -def _extract_feature(feature: ReportFeature, wasm_path: Path) -> ExtractedFeature: - result = feature.extract(wasm_path) - return ExtractedFeature(feature.name, [result]) - - -def _group_projects_by_folder( - project_paths: List[Path], -) -> Iterable[Tuple[Path, Iterable[Tuple[Path, Path]]]]: - path_pairs = sorted([(path.parent, path) for path in project_paths]) - return itertools.groupby(path_pairs, operator.itemgetter(0)) From b01227371f7f40fec5d3e13c1ab99363ce566b4e Mon Sep 17 00:00:00 2001 From: Bubu <2005901+BubuMVX@users.noreply.github.com> Date: Wed, 29 Jan 2025 22:33:25 +0100 Subject: [PATCH 06/84] Add output format: secret key in hexadecimal --- CLI.md | 2 +- multiversx_sdk_cli/cli_wallet.py | 15 ++++++++++++++- multiversx_sdk_cli/tests/test_cli_wallet.py | 11 +++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CLI.md b/CLI.md index 3cf655de..de4089e3 100644 --- a/CLI.md +++ b/CLI.md @@ -1850,7 +1850,7 @@ options: --outfile OUTFILE path to the output file --in-format {raw-mnemonic,keystore-mnemonic,keystore-secret-key,pem} the format of the input file - --out-format {raw-mnemonic,keystore-mnemonic,keystore-secret-key,pem,address-bech32,address-hex} + --out-format {raw-mnemonic,keystore-mnemonic,keystore-secret-key,pem,address-bech32,address-hex,secret-key} the format of the output file --address-index ADDRESS_INDEX the address index, if input format is raw-mnemonic, keystore-mnemonic or pem (with multiple entries) and the output format is keystore- diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index eeae9ebb..1d975884 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -24,6 +24,7 @@ WALLET_FORMAT_PEM = "pem" WALLET_FORMAT_ADDRESS_BECH32 = "address-bech32" WALLET_FORMAT_ADDRESS_HEX = "address-hex" +WALLET_FORMAT_SECRET_KEY = "secret-key" WALLET_FORMATS = [ WALLET_FORMAT_RAW_MNEMONIC, @@ -32,7 +33,12 @@ WALLET_FORMAT_PEM, ] -WALLET_FORMATS_AND_ADDRESSES = [*WALLET_FORMATS, WALLET_FORMAT_ADDRESS_BECH32, WALLET_FORMAT_ADDRESS_HEX] +WALLET_FORMATS_AND_ADDRESSES = [ + *WALLET_FORMATS, + WALLET_FORMAT_ADDRESS_BECH32, + WALLET_FORMAT_ADDRESS_HEX, + WALLET_FORMAT_SECRET_KEY, +] MAX_ITERATIONS_FOR_GENERATING_WALLET = 100 CURRENT_SHARDS = [i for i in range(NUMBER_OF_SHARDS)] @@ -280,6 +286,13 @@ def _create_wallet_content( pubkey = secret_key.generate_public_key() return pubkey.hex() + if out_format == WALLET_FORMAT_SECRET_KEY: + if mnemonic: + secret_key = mnemonic.derive_key(address_index) + assert secret_key is not None + + return secret_key.hex() + raise KnownError(f"Cannot create wallet, unknown output format: <{out_format}>. Make sure to use one of following: {WALLET_FORMATS}.") diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index 6121dbcc..9a6060f6 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -256,6 +256,17 @@ def test_wallet_convert_pem_to_pubkey(capsys: Any): assert out == "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1" +def test_wallet_convert_pem_to_secret_key(capsys: Any): + infile = testdata_path / "alice.pem" + + main([ + "wallet", "convert", "--infile", str(infile), "--in-format", "pem", "--out-format", "secret-key" + ]) + + out = _read_stdout(capsys).strip("Output:\n\n") + assert out == "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9" + + def test_wallet_sign_message(capsys: Any): message = "test" pem = testdata_path / "alice.pem" From c24aa4ce156875ee0474693583d4c7bb310ded37 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 30 Jan 2025 10:36:42 +0200 Subject: [PATCH 07/84] restore correct requirements --- pyproject.toml | 4 +--- requirements.txt | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a2fc4738..3ab9394d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,11 +24,9 @@ dependencies = [ "requests>=2.32.0,<3.0.0", "prettytable", "ledgercomm[hid]", - "semver", - "requests-cache", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk==0.19.0" + "multiversx-sdk==1.0.0" ] [project.scripts] diff --git a/requirements.txt b/requirements.txt index 0c7334d0..c047d5a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,9 +5,7 @@ types-requests prettytable types-prettytable ledgercomm[hid] -semver -requests-cache rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk==0.19.0 +multiversx-sdk==1.0.0 From fe53f40d7e657e3af3bb5b20da9d66a644643769 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 30 Jan 2025 13:43:54 +0200 Subject: [PATCH 08/84] remove some contract operations & rust dependency --- multiversx_sdk_cli/cli_contracts.py | 214 +---------------- multiversx_sdk_cli/dependencies/install.py | 2 - multiversx_sdk_cli/dependencies/modules.py | 216 +----------------- multiversx_sdk_cli/dependency_checker.py | 13 -- multiversx_sdk_cli/projects/__init__.py | 19 -- multiversx_sdk_cli/projects/constants.py | 2 - multiversx_sdk_cli/projects/core.py | 63 ----- multiversx_sdk_cli/projects/interfaces.py | 10 - multiversx_sdk_cli/projects/migrations.py | 23 -- multiversx_sdk_cli/projects/project_base.py | 195 ---------------- multiversx_sdk_cli/projects/project_rust.py | 117 ---------- multiversx_sdk_cli/projects/shared.py | 15 -- multiversx_sdk_cli/projects/templates.py | 56 ----- .../tests/test_cli_contracts.py | 76 +----- multiversx_sdk_cli/tests/test_cli_deps.py | 28 --- 15 files changed, 22 insertions(+), 1027 deletions(-) delete mode 100644 multiversx_sdk_cli/dependency_checker.py delete mode 100644 multiversx_sdk_cli/projects/__init__.py delete mode 100644 multiversx_sdk_cli/projects/constants.py delete mode 100644 multiversx_sdk_cli/projects/core.py delete mode 100644 multiversx_sdk_cli/projects/interfaces.py delete mode 100644 multiversx_sdk_cli/projects/migrations.py delete mode 100644 multiversx_sdk_cli/projects/project_base.py delete mode 100644 multiversx_sdk_cli/projects/project_rust.py delete mode 100644 multiversx_sdk_cli/projects/shared.py delete mode 100644 multiversx_sdk_cli/projects/templates.py diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 4ce518bd..ac721279 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -2,7 +2,7 @@ import logging import os from pathlib import Path -from typing import Any, List, Tuple +from typing import Any from multiversx_sdk import ( Address, @@ -13,25 +13,22 @@ ) from multiversx_sdk.abi import Abi -from multiversx_sdk_cli import cli_shared, projects, utils +from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS from multiversx_sdk_cli.contract_verification import trigger_contract_verification from multiversx_sdk_cli.contracts import SmartContract from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.dependency_checker import check_if_rust_is_installed from multiversx_sdk_cli.docker import is_docker_installed, run_docker from multiversx_sdk_cli.errors import DockerMissingError, NoWalletProvided from multiversx_sdk_cli.interfaces import IAddress -from multiversx_sdk_cli.projects.core import get_project_paths_recursively -from multiversx_sdk_cli.projects.templates import Contract -from multiversx_sdk_cli.ux import show_message, show_warning +from multiversx_sdk_cli.ux import show_warning logger = logging.getLogger("cli.contracts") -def setup_parser(args: List[str], subparsers: Any) -> Any: +def setup_parser(args: list[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser( subparsers, "contract", @@ -40,72 +37,14 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: subparsers = parser.add_subparsers() sub = cli_shared.add_command_subparser( - subparsers, - "contract", - "new", - "Create a new Smart Contract project based on a template.", - ) - sub.add_argument( - "--name", - help="The name of the contract. If missing, the name of the template will be used.", - ) - sub.add_argument("--template", required=True, help="the template to use") - sub.add_argument("--tag", help="the framework version on which the contract should be created") - sub.add_argument( - "--path", - type=str, - default=os.getcwd(), - help="the parent directory of the project (default: current directory)", + subparsers, "contract", "build", "Build a Smart Contract project. This command is DISABLED." ) - sub.set_defaults(func=create) - - sub = cli_shared.add_command_subparser( - subparsers, - "contract", - "templates", - "List the available Smart Contract templates.", - ) - sub.add_argument("--tag", help="the sc-meta framework version referred to") - sub.set_defaults(func=list_templates) - - sub = cli_shared.add_command_subparser(subparsers, "contract", "build", "Build a Smart Contract project.") - _add_build_options_sc_meta(sub) sub.set_defaults(func=build) - sub = cli_shared.add_command_subparser(subparsers, "contract", "clean", "Clean a Smart Contract project.") - sub.add_argument( - "--path", - default=os.getcwd(), - help="the project directory (default: current directory)", - ) - sub.set_defaults(func=clean) - - sub = cli_shared.add_command_subparser(subparsers, "contract", "test", "Run tests.") - sub.add_argument( - "--path", - default=os.getcwd(), - help="the directory of the contract (default: %(default)s)", - ) - sub.add_argument( - "--go", - action="store_true", - help="this arg runs rust and go tests (default: false)", - ) - sub.add_argument( - "--scen", - action="store_true", - help="this arg runs scenarios (default: false). If `--scen` and `--go` are both specified, scen overrides the go argument", - ) - sub.add_argument( - "--nocapture", - action="store_true", - help="this arg prints the entire output of the vm (default: false)", - ) - sub.set_defaults(func=run_tests) - output_description = CLIOutputBuilder.describe( with_contract=True, with_transaction_on_network=True, with_simulation=True ) + sub = cli_shared.add_command_subparser( subparsers, "contract", @@ -279,96 +218,6 @@ def _add_project_arg(sub: Any): ) -def _add_build_options_sc_meta(sub: Any): - sub.add_argument( - "--path", - default=os.getcwd(), - help="the project directory (default: current directory)", - ) - sub.add_argument( - "--no-wasm-opt", - action="store_true", - default=False, - help="do not optimize wasm files after the build (default: %(default)s)", - ) - sub.add_argument( - "--wasm-symbols", - action="store_true", - default=False, - help="for rust projects, does not strip the symbols from the wasm output. Useful for analysing the bytecode. Creates larger wasm files. Avoid in production (default: %(default)s)", - ) - sub.add_argument( - "--wasm-name", - type=str, - help="for rust projects, optionally specify the name of the wasm bytecode output file", - ) - sub.add_argument( - "--wasm-suffix", - type=str, - help="for rust projects, optionally specify the suffix of the wasm bytecode output file", - ) - sub.add_argument( - "--target-dir", - type=str, - help="for rust projects, forward the parameter to Cargo", - ) - sub.add_argument( - "--wat", - action="store_true", - help="also generate a WAT file when building", - default=False, - ) - sub.add_argument( - "--mir", - action="store_true", - help="also emit MIR files when building", - default=False, - ) - sub.add_argument( - "--llvm-ir", - action="store_true", - help="also emit LL (LLVM) files when building", - default=False, - ) - sub.add_argument("--ignore", help="ignore all directories with these names. [default: target]") - sub.add_argument( - "--no-imports", - action="store_true", - default=False, - help="skips extracting the EI imports after building the contracts", - ) - sub.add_argument( - "--no-abi-git-version", - action="store_true", - default=False, - help="skips loading the Git version into the ABI", - ) - sub.add_argument( - "--twiggy-top", - action="store_true", - default=False, - help="generate a twiggy top report after building", - ) - sub.add_argument( - "--twiggy-paths", - action="store_true", - default=False, - help="generate a twiggy paths report after building", - ) - sub.add_argument( - "--twiggy-monos", - action="store_true", - default=False, - help="generate a twiggy monos report after building", - ) - sub.add_argument( - "--twiggy-dominators", - action="store_true", - default=False, - help="generate a twiggy dominators report after building", - ) - - def _add_build_options_args(sub: Any): sub.add_argument( "--debug", @@ -475,52 +324,13 @@ def _add_metadata_arg(sub: Any): sub.set_defaults(metadata_upgradeable=True, metadata_payable=False) -def list_templates(args: Any): - tag = args.tag - contract = Contract(tag) - templates = contract.get_contract_templates() - show_message(templates) - - -def create(args: Any): - name = args.name - template = args.template - tag = args.tag - path = Path(args.path) - - contract = Contract(tag, name, template, path) - contract.create_from_template() - - -def get_project_paths(args: Any) -> List[Path]: - base_path = Path(args.project) - recursive = bool(args.recursive) - if recursive: - return get_project_paths_recursively(base_path) - return [base_path] - - -def clean(args: Any): - check_if_rust_is_installed() - project_path = args.path - projects.clean_project(Path(project_path)) - - def build(args: Any): - project_paths = [Path(args.path)] - arg_list = cli_shared.convert_args_object_to_args_list(args) - - for project in project_paths: - projects.build_project(project, arg_list) - - show_warning( - "The primary tool for building smart contracts is `sc-meta`. Try using the `sc-meta all build` command." - ) - + message = """This command cannot build smart contracts anymore. -def run_tests(args: Any): - check_if_rust_is_installed() - projects.run_tests(args) +The primary tool for building smart contracts is `sc-meta`. +To install `sc-meta` check out the documentation: https://docs.multiversx.com/sdk-and-tools/troubleshooting/rust-setup. +After installing, use the `sc-meta all build` command. To lear more about `sc-meta`, check out this page: https://docs.multiversx.com/developers/meta/sc-meta-cli/#calling-build.""" + show_warning(message) def deploy(args: Any): @@ -679,7 +489,7 @@ def query(args: Any): utils.dump_out_json(result) -def _get_contract_arguments(args: Any) -> Tuple[List[Any], bool]: +def _get_contract_arguments(args: Any) -> tuple[list[Any], bool]: json_args = json.loads(Path(args.arguments_file).expanduser().read_text()) if args.arguments_file else None if json_args and args.arguments: diff --git a/multiversx_sdk_cli/dependencies/install.py b/multiversx_sdk_cli/dependencies/install.py index e3666ab0..c9fb8d29 100644 --- a/multiversx_sdk_cli/dependencies/install.py +++ b/multiversx_sdk_cli/dependencies/install.py @@ -6,7 +6,6 @@ from multiversx_sdk_cli.dependencies.modules import ( DependencyModule, GolangModule, - Rust, TestWalletsModule, ) @@ -51,7 +50,6 @@ def get_deps_dict() -> Dict[str, DependencyModule]: def get_all_deps() -> List[DependencyModule]: return [ - Rust(key="rust"), GolangModule(key="golang"), TestWalletsModule(key="testwallets"), ] diff --git a/multiversx_sdk_cli/dependencies/modules.py b/multiversx_sdk_cli/dependencies/modules.py index 4437520e..3d2c4df9 100644 --- a/multiversx_sdk_cli/dependencies/modules.py +++ b/multiversx_sdk_cli/dependencies/modules.py @@ -1,31 +1,21 @@ import logging import os -import platform import shutil from os import path from pathlib import Path -from typing import Dict, List, Optional - -from multiversx_sdk_cli import ( - config, - dependencies, - downloader, - errors, - myprocess, - utils, - workstation, -) +from typing import Optional + +from multiversx_sdk_cli import config, downloader, errors, utils, workstation from multiversx_sdk_cli.dependencies.resolution import ( DependencyResolution, get_dependency_resolution, ) -from multiversx_sdk_cli.ux import show_message, show_warning logger = logging.getLogger("modules") class DependencyModule: - def __init__(self, key: str, aliases: List[str] = []): + def __init__(self, key: str, aliases: list[str] = []): self.key = key self.aliases = aliases @@ -64,7 +54,7 @@ def uninstall(self, tag: str) -> None: def is_installed(self, tag: str) -> bool: raise NotImplementedError() - def get_env(self) -> Dict[str, str]: + def get_env(self) -> dict[str, str]: raise NotImplementedError() def get_resolution(self) -> DependencyResolution: @@ -75,7 +65,7 @@ class StandaloneModule(DependencyModule): def __init__( self, key: str, - aliases: List[str] = [], + aliases: list[str] = [], repo_name: Optional[str] = None, organisation: Optional[str] = None, ): @@ -166,7 +156,7 @@ def is_installed(self, tag: str) -> bool: raise errors.BadDependencyResolution(self.key, resolution) - def get_env(self) -> Dict[str, str]: + def get_env(self) -> dict[str, str]: resolution = self.get_resolution() directory = self.get_directory(config.get_dependency_tag(self.key)) parent_directory = self.get_parent_directory() @@ -198,198 +188,6 @@ def get_gopath(self) -> Path: return self.get_parent_directory() / "GOPATH" -class Rust(DependencyModule): - def is_installed(self, tag: str) -> bool: - which_rustc = shutil.which("rustc") - which_cargo = shutil.which("cargo") - which_sc_meta = shutil.which("sc-meta") - which_mx_scenario_go = shutil.which("mx-scenario-go") - which_wasm_opt = shutil.which("wasm-opt") - which_twiggy = shutil.which("twiggy") - logger.info(f"which rustc: {which_rustc}") - logger.info(f"which cargo: {which_cargo}") - logger.info(f"which sc-meta: {which_sc_meta}") - logger.info(f"which mx-scenario-go: {which_mx_scenario_go}") - logger.info(f"which wasm-opt: {which_wasm_opt}") - logger.info(f"which twiggy: {which_twiggy}") - - dependencies = [ - which_rustc, - which_cargo, - which_sc_meta, - which_wasm_opt, - which_twiggy, - ] - installed = all(dependency is not None for dependency in dependencies) - - if installed: - actual_version_installed = self._get_actual_installed_version() - - if tag in actual_version_installed: - logger.info(f"[{self.key} {tag}] is installed.") - elif "not found" in actual_version_installed: - show_warning("You have installed Rust without using `rustup`.") - else: - show_warning( - f"The Rust version you have installed does not match the recommended version.\nInstalled [{actual_version_installed}], expected [{tag}]." - ) - - return installed - - def _get_actual_installed_version(self) -> str: - args = ["rustup", "default"] - output = myprocess.run_process(args, dump_to_stdout=False) - return output.strip() - - def install(self, overwrite: bool) -> None: - self._check_install_env(apply_correction=overwrite) - - module = dependencies.get_module_by_key("rust") - tag: str = config.get_dependency_tag(module.key) - - if not overwrite: - show_warning( - f"We recommend using rust {tag}. If you'd like to overwrite your current version please run `mxpy deps install rust --overwrite`." - ) - logger.info(f"install: key={self.key}, tag={tag}, overwrite={overwrite}") - - if overwrite: - logger.info("Overwriting the current rust version...") - elif self.is_installed(""): - return - - self._install_rust(tag) - self._install_sc_meta() - self._install_wasm_opt() - self._install_twiggy() - show_message( - "To ensure sc-meta functions correctly, please install all the required dependencies by executing the following command: `sc-meta install all`." - ) - - def _check_install_env(self, apply_correction: bool = True): - """ - See https://rust-lang.github.io/rustup/installation/index.html#choosing-where-to-install. - """ - - current_cargo_home = os.environ.get("CARGO_HOME", None) - current_rustup_home = os.environ.get("RUSTUP_HOME", None) - if current_cargo_home: - show_warning( - f"""CARGO_HOME variable is set to: {current_cargo_home}. -This may cause problems with the installation.""" - ) - - if apply_correction: - show_warning("CARGO_HOME will be temporarily unset.") - os.environ["CARGO_HOME"] = "" - - if current_rustup_home: - show_warning( - f"""RUSTUP_HOME variable is set to: {current_rustup_home}. -This may cause problems with the installation of rust.""" - ) - - if apply_correction: - show_warning("RUSTUP_HOME will be temporarily unset.") - os.environ["RUSTUP_HOME"] = "" - - def _install_rust(self, tag: str) -> None: - installer_url = self._get_installer_url() - installer_path = self._get_installer_path() - - downloader.download(installer_url, str(installer_path)) - utils.mark_executable(str(installer_path)) - - if tag: - toolchain = tag - else: - toolchain = "stable" - - args = [ - str(installer_path), - "--verbose", - "--default-toolchain", - toolchain, - "--profile", - "minimal", - "--target", - "wasm32-unknown-unknown", - "-y", - ] - - logger.info("Installing rust.") - myprocess.run_process(args) - - def _install_sc_meta(self): - logger.info("Installing multiversx-sc-meta.") - tag = config.get_dependency_tag("sc-meta") - args = ["cargo", "install", "multiversx-sc-meta", "--locked"] - - if tag: - args.extend(["--version", tag]) - - myprocess.run_process(args, env=self.get_cargo_env()) - - def _install_wasm_opt(self): - logger.info("Installing wasm-opt. This may take a while.") - tag = config.get_dependency_tag("wasm-opt") - args = ["cargo", "install", "wasm-opt", "--version", tag] - myprocess.run_process(args, env=self.get_cargo_env()) - - def _install_twiggy(self): - logger.info("Installing twiggy.") - tag = config.get_dependency_tag("twiggy") - args = ["cargo", "install", "twiggy"] - - if tag: - args.extend(["--version", tag]) - - myprocess.run_process(args, env=self.get_cargo_env()) - - def _install_sc_meta_deps(self): - # this is needed for sc-meta to run the tests - # if os is Windows skip installation - os = platform.system().lower() - if os == "windows": - logger.warning("`sc-meta install all` command is not supported on windows") - return - - logger.info("Installing sc-meta dependencies.") - args = ["sc-meta", "install", "all"] - myprocess.run_process(args) - - def _get_installer_url(self) -> str: - if workstation.is_windows(): - return "https://win.rustup.rs" - return "https://sh.rustup.rs" - - def _get_installer_path(self) -> Path: - tools_folder = workstation.get_tools_folder() - - if workstation.is_windows(): - return tools_folder / "rustup-init.exe" - return tools_folder / "rustup.sh" - - def uninstall(self, tag: str): - directory = self.get_directory("") - if os.path.isdir(directory): - shutil.rmtree(directory) - - def get_directory(self, tag: str) -> Path: - tools_folder = workstation.get_tools_folder() - return tools_folder / "vendor-rust" - - def get_env(self) -> Dict[str, str]: - return dict(os.environ) - - def get_cargo_env(self) -> Dict[str, str]: - env = self.get_env() - cargo = Path("~/.cargo/bin").expanduser() - path = env["PATH"] - env["PATH"] = f"{str(cargo)}:{path}" - return env - - class TestWalletsModule(StandaloneModule): def __init__(self, key: str): super().__init__(key, []) diff --git a/multiversx_sdk_cli/dependency_checker.py b/multiversx_sdk_cli/dependency_checker.py deleted file mode 100644 index abf392a4..00000000 --- a/multiversx_sdk_cli/dependency_checker.py +++ /dev/null @@ -1,13 +0,0 @@ -from multiversx_sdk_cli import config, errors, ux -from multiversx_sdk_cli.dependencies.modules import Rust - - -def check_if_rust_is_installed(): - RUST_MODULE_KEY = "rust" - rust_module = Rust(RUST_MODULE_KEY) - if not rust_module.is_installed(""): - tag = config.get_dependency_tag(RUST_MODULE_KEY) - ux.show_critical_error( - "Rust is not installed on your machine. Run `mxpy deps install rust --overwrite` and try again." - ) - raise errors.DependencyMissing(RUST_MODULE_KEY, tag) diff --git a/multiversx_sdk_cli/projects/__init__.py b/multiversx_sdk_cli/projects/__init__.py deleted file mode 100644 index d562dc41..00000000 --- a/multiversx_sdk_cli/projects/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -from multiversx_sdk_cli.projects.core import ( - build_project, - clean_project, - load_project, - run_tests, -) -from multiversx_sdk_cli.projects.project_base import Project -from multiversx_sdk_cli.projects.project_rust import ProjectRust -from multiversx_sdk_cli.projects.templates import Contract - -__all__ = [ - "build_project", - "clean_project", - "run_tests", - "load_project", - "Project", - "ProjectRust", - "Contract", -] diff --git a/multiversx_sdk_cli/projects/constants.py b/multiversx_sdk_cli/projects/constants.py deleted file mode 100644 index 0ee36a27..00000000 --- a/multiversx_sdk_cli/projects/constants.py +++ /dev/null @@ -1,2 +0,0 @@ -PROJECT_CONFIG_FILENAME = "multiversx.json" -OLD_PROJECT_CONFIG_FILENAME = "elrond.json" diff --git a/multiversx_sdk_cli/projects/core.py b/multiversx_sdk_cli/projects/core.py deleted file mode 100644 index fbcfba3f..00000000 --- a/multiversx_sdk_cli/projects/core.py +++ /dev/null @@ -1,63 +0,0 @@ -import logging -from pathlib import Path -from typing import Any, List - -from multiversx_sdk_cli import errors, guards -from multiversx_sdk_cli.projects import shared -from multiversx_sdk_cli.projects.constants import ( - OLD_PROJECT_CONFIG_FILENAME, - PROJECT_CONFIG_FILENAME, -) -from multiversx_sdk_cli.projects.project_base import Project -from multiversx_sdk_cli.projects.project_rust import ProjectRust - -logger = logging.getLogger("projects.core") - - -def load_project(directory: Path) -> Project: - guards.is_directory(directory) - - if shared.is_source_rust(directory): - return ProjectRust(directory) - else: - raise errors.NotSupportedProject(str(directory)) - - -def build_project(directory: Path, args: List[str]): - directory = directory.expanduser() - - logger.info("build_project.directory: %s", directory) - - project = load_project(directory) - outputs = project.build(args) - logger.info("Build ran.") - for output_wasm_file in outputs: - logger.info(f"WASM file generated: {output_wasm_file}") - - -def clean_project(directory: Path): - logger.info("clean_project.directory: %s", directory) - directory = directory.expanduser() - guards.is_directory(directory) - project = load_project(directory) - project.clean() - logger.info("Project cleaned.") - - -def run_tests(args: Any): - directory = Path(args.path) - - logger.info("run_tests.project: %s", directory) - - guards.is_directory(directory) - project = load_project(directory) - project.run_tests(args) - - -def get_project_paths_recursively(base_path: Path) -> List[Path]: - guards.is_directory(base_path) - old_markers = list(base_path.glob(f"**/{OLD_PROJECT_CONFIG_FILENAME}")) - new_markers = list(base_path.glob(f"**/{PROJECT_CONFIG_FILENAME}")) - project_marker_files = old_markers + new_markers - path_list = [marker_file.parent for marker_file in project_marker_files] - return sorted(path_list) diff --git a/multiversx_sdk_cli/projects/interfaces.py b/multiversx_sdk_cli/projects/interfaces.py deleted file mode 100644 index 3a14fa5d..00000000 --- a/multiversx_sdk_cli/projects/interfaces.py +++ /dev/null @@ -1,10 +0,0 @@ -from pathlib import Path -from typing import Any - - -class IProject: - def get_option(self, option_name: str) -> Any: - return None - - def get_file_wasm(self) -> Path: - return Path("") diff --git a/multiversx_sdk_cli/projects/migrations.py b/multiversx_sdk_cli/projects/migrations.py deleted file mode 100644 index b0ddda1a..00000000 --- a/multiversx_sdk_cli/projects/migrations.py +++ /dev/null @@ -1,23 +0,0 @@ -from pathlib import Path - -from multiversx_sdk_cli.projects.constants import ( - OLD_PROJECT_CONFIG_FILENAME, - PROJECT_CONFIG_FILENAME, -) - - -def migrate_project_config_file(project_path: Path): - new_config_file = project_path / PROJECT_CONFIG_FILENAME - old_config_file = project_path / OLD_PROJECT_CONFIG_FILENAME - - if old_config_file.exists(): - if new_config_file.exists(): - old_config_file.unlink() - else: - old_config_file.rename(new_config_file) - - -def migrate_project_templates(parent_folder: Path): - for project_folder in parent_folder.iterdir(): - if project_folder.is_dir(): - migrate_project_config_file(project_folder) diff --git a/multiversx_sdk_cli/projects/project_base.py b/multiversx_sdk_cli/projects/project_base.py deleted file mode 100644 index dd1a2e8a..00000000 --- a/multiversx_sdk_cli/projects/project_base.py +++ /dev/null @@ -1,195 +0,0 @@ -import logging -import shutil -from abc import abstractmethod -from os import path -from pathlib import Path -from typing import Any, Dict, List, Union, final - -from multiversx_sdk_cli import dependencies, errors, myprocess, utils -from multiversx_sdk_cli.projects.constants import PROJECT_CONFIG_FILENAME -from multiversx_sdk_cli.projects.interfaces import IProject -from multiversx_sdk_cli.projects.migrations import migrate_project_config_file - -logger = logging.getLogger("Project") - - -class Project(IProject): - - def __init__(self, directory: Path): - self.path = directory.expanduser().resolve() - self.directory = str(self.path) - - def build(self, args: List[str], options: Union[Dict[str, Any], None] = None) -> List[Path]: - migrate_project_config_file(self.path) - self.forwarded_args = args - self.options = options or dict() - self.debug = self.options.get("debug", False) - self._ensure_dependencies_installed() - self.perform_build() - contract_paths = self._do_after_build_custom() - self._do_after_build_core() - return contract_paths - - def clean(self): - utils.remove_folder(self.get_output_folder()) - - def _ensure_dependencies_installed(self): - module_keys = self.get_dependencies() - for module_key in module_keys: - if module_key == "": - continue - dependencies.install_module(module_key) - - def get_dependencies(self) -> List[str]: - raise NotImplementedError() - - def perform_build(self) -> None: - raise NotImplementedError() - - def get_file_wasm(self) -> Path: - return self.find_file_in_output("*.wasm") - - def find_file_globally(self, pattern: str) -> Path: - return self.find_file_in_folder(self.path, pattern) - - def find_file_in_output(self, pattern: str) -> Path: - folder = self.get_output_folder() - return self.find_file_in_folder(Path(folder), pattern) - - def find_file_in_folder(self, folder: Path, pattern: str) -> Path: - files = list(folder.rglob(pattern)) - - if len(files) == 0: - raise errors.KnownError(f"No file matches pattern [{pattern}].") - if len(files) > 1: - logger.warning(f"More files match pattern [{pattern}]. Will pick first:\n{files}") - - file = folder / files[0] - return Path(file).resolve() - - def find_wasm_files(self): - output_folder = Path(self.get_output_folder()) - wasm_files = output_folder.rglob("*.wasm") - main_wasm_files = list(filter(lambda wasm_path: not wasm_path.name.endswith("-dbg.wasm"), wasm_files)) - return main_wasm_files - - @abstractmethod - def _do_after_build_custom(self) -> List[Path]: - raise NotImplementedError() - - @final - def _do_after_build_core(self): - pass - - def _copy_to_output(self, source: Path, destination: Union[str, None] = None) -> Path: - output_folder = self.get_output_folder() - utils.ensure_folder(output_folder) - destination = path.join(output_folder, destination) if destination else output_folder - output_wasm_file = shutil.copy(str(source), destination) - return Path(output_wasm_file) - - def get_output_folder(self): - return path.join(self.directory, "output") - - def get_wasm_default_name(self, suffix: str = "") -> str: - return f"{self.path.name}{suffix}.wasm" - - def get_wasm_path(self, wasm_name: str) -> Path: - return Path(self.get_output_folder(), wasm_name).resolve() - - def get_wasm_default_path(self) -> Path: - return self.get_wasm_path(self.get_wasm_default_name()) - - def get_wasm_view_default_path(self) -> Path: - return self.get_wasm_path(self.get_wasm_default_name("-view")) - - def get_bytecode(self): - bytecode: bytes = self.get_file_wasm().read_bytes() - bytecode_hex = bytecode.hex() - return bytecode_hex - - def load_config(self): - self.ensure_config_file() - config_file = self.get_config_file() - config = utils.read_json_file(str(config_file)) - return config - - def get_config_file(self) -> Path: - return self.path / PROJECT_CONFIG_FILENAME - - def ensure_config_file(self) -> None: - config_file = self.get_config_file() - - if not config_file.exists(): - utils.write_json_file(str(config_file), self.default_config()) - logger.info("created default project configuration") - - def default_config(self) -> Dict[str, Any]: - return dict() - - def run_tests(self, args: Any): - command = ["sc-meta", "test"] - - if args.path: - command.extend(["--path", str(Path(args.path).expanduser())]) - - if args.go: - command.append("--go") - - if args.scen: - command.append("--scen") - - if args.nocapture: - command.append("--nocapture") - - myprocess.run_process(command) - - -def glob_files(folder: Path, pattern: str) -> List[Path]: - files = folder.rglob(pattern) - return [Path(folder / file).resolve() for file in files] - - -def exclude_files(files: List[Path], to_exclude: List[Path]) -> List[Path]: - return list(set(files).difference(to_exclude)) - - -def rename_wasm_files(paths: List[Path], name: Union[str, None]) -> List[Path]: - if name is None: - return paths - new_paths = [adjust_wasm_filename(path, name) for path in paths] - for old_path, new_path in zip(paths, new_paths): - old_path.rename(new_path) - return new_paths - - -def get_contract_suffix(name: str) -> str: - for suffix in ["-view.wasm", ".wasm"]: - if name.endswith(suffix): - return suffix - return "" - - -def remove_suffix(name: str, suffix: str) -> str: - if not name.endswith(suffix) or len(suffix) == 0: - return name - return name[: -len(suffix)] - - -def adjust_wasm_filename(path: Path, name_hint: str) -> Path: - """ - Adjusts the wasm's filename by using a name hint - - >>> adjust_wasm_filename(Path("test/my-contract.wasm"), "hello.wasm") - PosixPath('test/hello.wasm') - >>> adjust_wasm_filename(Path("test/my-contract-view.wasm"), "hello.wasm") - PosixPath('test/hello-view.wasm') - >>> adjust_wasm_filename(Path("test/my-contract-view.wasm"), "hello") - PosixPath('test/hello-view.wasm') - >>> adjust_wasm_filename(Path("test/my-contract.wasm"), "world-view.wasm") - PosixPath('test/world-view.wasm') - >>> adjust_wasm_filename(Path("test/my-contract-view.wasm"), "world-view.wasm") - PosixPath('test/world-view-view.wasm') - """ - new_name = remove_suffix(name_hint, ".wasm") + get_contract_suffix(path.name) - return path.with_name(new_name) diff --git a/multiversx_sdk_cli/projects/project_rust.py b/multiversx_sdk_cli/projects/project_rust.py deleted file mode 100644 index fba6849e..00000000 --- a/multiversx_sdk_cli/projects/project_rust.py +++ /dev/null @@ -1,117 +0,0 @@ -import logging -import subprocess -from pathlib import Path -from typing import Any, Dict, List - -from multiversx_sdk_cli import dependencies, errors, utils, workstation -from multiversx_sdk_cli.constants import DEFAULT_CARGO_TARGET_DIR_NAME -from multiversx_sdk_cli.dependencies.modules import Rust -from multiversx_sdk_cli.projects.project_base import Project - -logger = logging.getLogger("ProjectRust") - - -class ProjectRust(Project): - def __init__(self, directory: Path): - super().__init__(directory) - - def clean(self): - env = self.get_env() - - args = ["sc-meta", "all", "clean", "--path", self.directory] - - subprocess.check_call(args, env=env) - - def get_meta_folder(self): - return self.path / "meta" - - def get_wasm_view_folder(self): - return self.path / "wasm-view" - - def perform_build(self): - meta = self.has_meta() - if not meta: - raise errors.NotSupportedProject("The project does not have a meta crate") - - self.run_meta() - - def prepare_build_wasm_args(self, args: List[str]): - args.extend( - [ - "--target=wasm32-unknown-unknown", - "--release", - "--out-dir", - self.get_output_folder(), - ] - ) - - def run_meta(self): - env = self.get_env() - - args = ["sc-meta", "all", "build"] - - args.extend(self.forwarded_args) - - try: - subprocess.check_call(args, env=env) - except subprocess.CalledProcessError as err: - raise errors.BuildError(f"error code = {err.returncode}, see output") - - def _ensure_cargo_target_dir(self, target_dir: str): - default_target_dir = str(workstation.get_tools_folder() / DEFAULT_CARGO_TARGET_DIR_NAME) - target_dir = target_dir or default_target_dir - utils.ensure_folder(target_dir) - return target_dir - - def has_meta(self): - return (self.get_meta_folder() / "Cargo.toml").exists() - - def has_wasm_view(self): - return (self.get_wasm_view_folder() / "Cargo.toml").exists() - - def has_abi(self): - return (self.get_abi_folder() / "Cargo.toml").exists() - - def get_abi_filepath(self): - return self.get_abi_folder() / "abi.json" - - def get_abi_folder(self): - return Path(self.directory, "abi") - - def _do_after_build_custom(self) -> List[Path]: - outputs = [self.get_wasm_default_path()] - if self.has_wasm_view(): - outputs.append(self.get_wasm_view_default_path()) - return outputs - - def get_dependencies(self): - return ["rust"] - - def get_env(self): - return dependencies.get_module_by_key("rust").get_env() - - def build_wasm_with_debug_symbols(self, build_options: Dict[str, Any]): - rust_module = Rust("rust") - rust_module.install(overwrite=False) - - cwd = self.path - env = self.get_env() - target_dir: str = build_options.get("target-dir", "") - target_dir = self._ensure_cargo_target_dir(target_dir) - - args = [ - "sc-meta", - "all", - "build", - "--wasm-symbols", - "--wasm-suffix", - "dbg", - "--no-wasm-opt", - "--target-dir", - target_dir, - ] - - try: - subprocess.check_call(args, env=env, cwd=cwd) - except subprocess.CalledProcessError as err: - raise errors.BuildError(f"error code = {err.returncode}, see output") diff --git a/multiversx_sdk_cli/projects/shared.py b/multiversx_sdk_cli/projects/shared.py deleted file mode 100644 index 25cdbfd4..00000000 --- a/multiversx_sdk_cli/projects/shared.py +++ /dev/null @@ -1,15 +0,0 @@ -import logging -from pathlib import Path - -logger = logging.getLogger("projects.shared") - - -def is_source_rust(directory: Path) -> bool: - return _directory_contains_file(directory, "Cargo.toml") - - -def _directory_contains_file(directory: Path, name_suffix: str) -> bool: - for file in directory.iterdir(): - if str(file).lower().endswith(name_suffix.lower()): - return True - return False diff --git a/multiversx_sdk_cli/projects/templates.py b/multiversx_sdk_cli/projects/templates.py deleted file mode 100644 index ae487303..00000000 --- a/multiversx_sdk_cli/projects/templates.py +++ /dev/null @@ -1,56 +0,0 @@ -import logging -from pathlib import Path -from typing import List, Union - -from multiversx_sdk_cli import myprocess -from multiversx_sdk_cli.dependency_checker import check_if_rust_is_installed - -logger = logging.getLogger("projects.templates") - - -class Contract: - def __init__( - self, - tag: Union[str, None] = None, - name: Union[str, None] = None, - template: str = "", - path: Path = Path(), - ) -> None: - self.tag = tag - self.name = name - self.template = template - self.path = path - - def get_contract_templates(self) -> str: - self._ensure_dependencies_installed() - args = self._prepare_args_to_list_templates() - templates = myprocess.run_process(args=args, dump_to_stdout=False) - return templates - - def create_from_template(self) -> None: - self._ensure_dependencies_installed() - args = self._prepare_args_to_create_new_contract_from_template() - myprocess.run_process(args) - - def _ensure_dependencies_installed(self): - logger.info("Checking if the necessarry dependencies are installed.") - check_if_rust_is_installed() - - def _prepare_args_to_list_templates(self) -> List[str]: - args = ["sc-meta", "templates"] - - if self.tag: - args.extend(["--tag", self.tag]) - - return args - - def _prepare_args_to_create_new_contract_from_template(self) -> List[str]: - args = ["sc-meta", "new", "--template", self.template, "--path", str(self.path)] - - if self.name: - args.extend(["--name", self.name]) - - if self.tag: - args.extend(["--tag", self.tag]) - - return args diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index 887d32b6..3679e062 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -2,85 +2,15 @@ from pathlib import Path from typing import Any -import pytest - from multiversx_sdk_cli.cli import main parent = Path(__file__).parent +(parent / "testdata-out").mkdir(exist_ok=True) -def test_contract_new(): - main( - [ - "contract", - "new", - "--template", - "adder", - "--path", - f"{parent}/testdata-out/SANDBOX", - ] - ) - assert Path.is_dir(parent / "testdata-out" / "SANDBOX" / "adder") - - -def test_contract_new_with_bad_code(): - # we change the contract code so the build would fail so we can catch the error - main( - [ - "contract", - "new", - "--template", - "adder", - "--path", - f"{parent}/testdata-out/SANDBOX", - "--name", - "adder-bad-src", - ] - ) - - assert Path.is_dir(parent / "testdata-out" / "SANDBOX" / "adder-bad-src") - replace_variable_with_unknown_variable_for_adder() - - -def replace_variable_with_unknown_variable_for_adder(): - # this is done in order to replace the value added in the adder contract with a unknown variable - with open( - parent / "testdata-out" / "SANDBOX" / "adder-bad-src" / "src" / "adder_bad_src.rs", - "r", - ) as f: - contract_lines = f.readlines() - - for index, line in reversed(list(enumerate(contract_lines))): - if "value" in line: - contract_lines[index] = line.replace("value", "unknown_variable") - break - - with open( - parent / "testdata-out" / "SANDBOX" / "adder-bad-src" / "src" / "adder_bad_src.rs", - "w", - ) as f: - f.writelines(contract_lines) - - -@pytest.mark.skip_on_windows def test_contract_build(): - main(["contract", "build", "--path", f"{parent}/testdata-out/SANDBOX/adder"]) - - assert Path.is_file(parent / "testdata-out" / "SANDBOX" / "adder" / "output" / "adder.wasm") - - -@pytest.mark.skip_on_windows -def test_bad_contract_build(capsys: Any): - ERROR = "Build error: error code = 101, see output." - - main(["contract", "build", "--path", f"{parent}/testdata-out/SANDBOX/adder-bad-src"]) - - out, _ = capsys.readouterr() - - if ERROR in out: - assert True - else: - assert False + return_code = main(["contract", "build"]) + assert not return_code def test_contract_deploy(): diff --git a/multiversx_sdk_cli/tests/test_cli_deps.py b/multiversx_sdk_cli/tests/test_cli_deps.py index 63162d71..8c131656 100644 --- a/multiversx_sdk_cli/tests/test_cli_deps.py +++ b/multiversx_sdk_cli/tests/test_cli_deps.py @@ -1,36 +1,8 @@ -import shutil -from pathlib import Path - import pytest from multiversx_sdk_cli.cli import main -def test_deps_install_rust(): - return_code = main(["deps", "install", "rust", "--overwrite"]) - assert return_code == 0 - - -def test_deps_check_rust(): - return_code = main(["deps", "check", "rust"]) - assert True if return_code == 0 else False - - which_rustc = shutil.which("rustc") - assert which_rustc and Path.is_file(Path(which_rustc)) - - which_cargo = shutil.which("cargo") - assert which_cargo and Path.is_file(Path(which_cargo)) - - which_sc_meta = shutil.which("sc-meta") - assert which_sc_meta and Path.is_file(Path(which_sc_meta)) - - which_wasm_opt = shutil.which("wasm-opt") - assert which_wasm_opt and Path.is_file(Path(which_wasm_opt)) - - which_twiggy = shutil.which("twiggy") - assert which_twiggy and Path.is_file(Path(which_twiggy)) - - def test_deps_install_testwallets(): return_code = main(["deps", "install", "testwallets"]) assert return_code == 0 From 83ea60fd54b68051c2c312af8e0fa15c3219bab5 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 30 Jan 2025 14:06:14 +0200 Subject: [PATCH 09/84] update GH actions --- .github/workflows/build-windows.yml | 8 ++++++-- .github/workflows/build.yml | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index b157b6e8..fda48f40 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -38,11 +38,15 @@ jobs: export PYTHONPATH=. python3 -m multiversx_sdk_cli.cli config new test python3 -m multiversx_sdk_cli.cli config set github_api_token ${{ secrets.GITHUB_TOKEN }} + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: stable + target: wasm32-unknown-unknown - name: Setup test dependencies shell: bash run: | python3 -m multiversx_sdk_cli.cli deps install testwallets - python3 -m multiversx_sdk_cli.cli deps install rust - name: Run unit tests shell: bash run: | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 044f4b88..bba1e891 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -36,10 +36,14 @@ jobs: export PYTHONPATH=. python3 -m multiversx_sdk_cli.cli config new test python3 -m multiversx_sdk_cli.cli config set github_api_token ${{ secrets.GITHUB_TOKEN }} + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: stable + target: wasm32-unknown-unknown - name: Setup test dependencies run: | python3 -m multiversx_sdk_cli.cli deps install testwallets - python3 -m multiversx_sdk_cli.cli deps install rust - name: Run unit tests run: | export PYTHONPATH=. From 1b449172ae9713c7944887a635e121d4ca05d844 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 31 Jan 2025 14:01:20 +0200 Subject: [PATCH 10/84] refactor smart contract interactions --- multiversx_sdk_cli/cli_contracts.py | 118 ++++++++++++++++--------- multiversx_sdk_cli/cli_delegation.py | 1 + multiversx_sdk_cli/cli_dns.py | 1 + multiversx_sdk_cli/cli_password.py | 7 ++ multiversx_sdk_cli/cli_shared.py | 72 +++++++++++++-- multiversx_sdk_cli/cli_transactions.py | 3 + multiversx_sdk_cli/cli_validators.py | 1 + multiversx_sdk_cli/contracts.py | 36 ++++---- 8 files changed, 179 insertions(+), 60 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index ac721279..53f15107 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -2,7 +2,7 @@ import logging import os from pathlib import Path -from typing import Any +from typing import Any, Union from multiversx_sdk import ( Address, @@ -21,8 +21,8 @@ from multiversx_sdk_cli.contracts import SmartContract from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.docker import is_docker_installed, run_docker -from multiversx_sdk_cli.errors import DockerMissingError, NoWalletProvided -from multiversx_sdk_cli.interfaces import IAddress +from multiversx_sdk_cli.errors import DockerMissingError +from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.ux import show_warning logger = logging.getLogger("cli.contracts") @@ -32,15 +32,10 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser( subparsers, "contract", - "Build, deploy, upgrade and interact with Smart Contracts", + "Deploy, upgrade and interact with Smart Contracts", ) subparsers = parser.add_subparsers() - sub = cli_shared.add_command_subparser( - subparsers, "contract", "build", "Build a Smart Contract project. This command is DISABLED." - ) - sub.set_defaults(func=build) - output_description = CLIOutputBuilder.describe( with_contract=True, with_transaction_on_network=True, with_simulation=True ) @@ -72,6 +67,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) sub.set_defaults(func=deploy) @@ -103,6 +99,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) sub.set_defaults(func=call) @@ -134,6 +131,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) sub.set_defaults(func=upgrade) @@ -205,6 +203,11 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: ) sub.set_defaults(func=do_reproducible_build) + sub = cli_shared.add_command_subparser( + subparsers, "contract", "build", "Build a Smart Contract project. This command is DISABLED." + ) + sub.set_defaults(func=build) + parser.epilog = cli_shared.build_group_epilog(subparsers) return subparsers @@ -274,7 +277,7 @@ def _add_contract_arg(sub: Any): def _add_contract_abi_arg(sub: Any): - sub.add_argument("--abi", type=str, help="the ABI of the Smart Contract") + sub.add_argument("--abi", type=str, help="the ABI file of the Smart Contract") def _add_function_arg(sub: Any): @@ -338,9 +341,20 @@ def deploy(args: Any): cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_chain_id_in_args(args) - cli_shared.prepare_nonce_in_args(args) sender = cli_shared.prepare_account(args) + + if not args.nonce: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + config = TransactionsFactoryConfig(args.chain) abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) @@ -358,15 +372,17 @@ def deploy(args: Any): payable_by_sc=args.metadata_payable_by_sc, gas_limit=int(args.gas_limit), value=int(args.value), - nonce=int(args.nonce), + nonce=nonce, version=int(args.version), options=int(args.options), - guardian=args.guardian, + guardian=guardian_address, + relayer=relayer_address, ) - tx = _sign_guarded_tx(args, tx) + tx = _sign_guarded_tx(guardian, args, tx) + _sign_tx_by_relayer(relayer, tx) address_computer = AddressComputer(NUMBER_OF_SHARDS) - contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=args.nonce) + contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=tx.nonce) logger.info("Contract address: %s", contract_address.to_bech32()) utils.log_explorer_contract_address(args.chain, contract_address.to_bech32()) @@ -374,29 +390,39 @@ def deploy(args: Any): _send_or_simulate(tx, contract_address, args) -def _sign_guarded_tx(args: Any, tx: Transaction) -> Transaction: - try: - guardian_account = cli_shared.prepare_guardian_account(args) - except NoWalletProvided: - guardian_account = None - - if guardian_account: - tx.guardian_signature = bytes.fromhex(guardian_account.sign_transaction(tx)) - elif args.guardian: - tx = cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) # type: ignore +def _sign_guarded_tx(guardian: Union[IAccount, None], args: Any, tx: Transaction) -> Transaction: + if guardian: + tx.guardian_signature = bytes.fromhex(guardian.sign_transaction(tx)) + elif tx.guardian and args.guardian_service_url and args.guardian_2fa_code: + tx = cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) return tx +def _sign_tx_by_relayer(relayer: Union[IAccount, None], tx: Transaction): + if relayer and tx.relayer: + tx.relayer_signature = bytes.fromhex(relayer.sign_transaction(tx)) + + def call(args: Any): logger.debug("call") cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_chain_id_in_args(args) - cli_shared.prepare_nonce_in_args(args) sender = cli_shared.prepare_account(args) + if not args.nonce: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + config = TransactionsFactoryConfig(args.chain) abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) @@ -413,12 +439,14 @@ def call(args: Any): gas_limit=int(args.gas_limit), value=int(args.value), transfers=args.token_transfers, - nonce=int(args.nonce), + nonce=nonce, version=int(args.version), options=int(args.options), - guardian=args.guardian, + guardian=guardian_address, + relayer=relayer_address, ) - tx = _sign_guarded_tx(args, tx) + tx = _sign_guarded_tx(guardian, args, tx) + _sign_tx_by_relayer(relayer, tx) _send_or_simulate(tx, contract_address, args) @@ -428,9 +456,20 @@ def upgrade(args: Any): cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_chain_id_in_args(args) - cli_shared.prepare_nonce_in_args(args) sender = cli_shared.prepare_account(args) + + if not args.nonce: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + config = TransactionsFactoryConfig(args.chain) abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) @@ -450,12 +489,14 @@ def upgrade(args: Any): payable_by_sc=args.metadata_payable_by_sc, gas_limit=int(args.gas_limit), value=int(args.value), - nonce=int(args.nonce), + nonce=nonce, version=int(args.version), options=int(args.options), - guardian=args.guardian, + guardian=guardian_address, + relayer=relayer_address, ) - tx = _sign_guarded_tx(args, tx) + tx = _sign_guarded_tx(guardian, args, tx) + _sign_tx_by_relayer(relayer, tx) _send_or_simulate(tx, contract_address, args) @@ -463,11 +504,8 @@ def upgrade(args: Any): def query(args: Any): logger.debug("query") - # workaround so we can use the function below to set chainID - args.chain = "" - cli_shared.prepare_chain_id_in_args(args) - - factory_config = TransactionsFactoryConfig(args.chain) + # we don't need chainID to query a contract; we use the provided proxy + factory_config = TransactionsFactoryConfig("") abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(factory_config, abi) @@ -504,14 +542,14 @@ def _get_contract_arguments(args: Any) -> tuple[list[Any], bool]: return args.arguments, True -def _send_or_simulate(tx: Transaction, contract_address: IAddress, args: Any): +def _send_or_simulate(tx: Transaction, contract_address: Address, args: Any): output_builder = cli_shared.send_or_simulate(tx, args, dump_output=False) output_builder.set_contract_address(contract_address) utils.dump_out_json(output_builder.build(), outfile=args.outfile) def verify(args: Any) -> None: - contract = Address.from_bech32(args.contract) + contract = Address.new_from_bech32(args.contract) verifier_url = args.verifier_url packaged_src = Path(args.packaged_src).expanduser().resolve() diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index f5d409f7..c00c2869 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -359,6 +359,7 @@ def _add_common_arguments(args: List[str], sub: Any): cli_shared.add_broadcast_args(sub) cli_shared.add_outfile_arg(sub, what="signed transaction, hash") cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) def ensure_arguments_are_provided_and_prepared(args: Any): diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index e4be41c0..1e890b50 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -35,6 +35,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) sub.add_argument("--name", help="the name to register") sub.set_defaults(func=register) diff --git a/multiversx_sdk_cli/cli_password.py b/multiversx_sdk_cli/cli_password.py index d7fff56b..98666c31 100644 --- a/multiversx_sdk_cli/cli_password.py +++ b/multiversx_sdk_cli/cli_password.py @@ -14,3 +14,10 @@ def load_guardian_password(args: Any) -> str: with open(args.guardian_passfile) as pass_file: return pass_file.read().strip() return getpass("Keyfile's password: ") + + +def load_relayer_password(args: Any) -> str: + if args.relayer_passfile: + with open(args.relayer_passfile) as pass_file: + return pass_file.read().strip() + return getpass("Keyfile's password: ") diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 443b3956..67f77c7d 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -3,14 +3,18 @@ import copy import sys from argparse import FileType -from typing import Any, Dict, List, Text, cast +from typing import Any, Dict, List, Text, Union, cast from multiversx_sdk import Address, ProxyNetworkProvider, Transaction from multiversx_sdk_cli import config, errors, utils from multiversx_sdk_cli.accounts import Account, LedgerAccount from multiversx_sdk_cli.cli_output import CLIOutputBuilder -from multiversx_sdk_cli.cli_password import load_guardian_password, load_password +from multiversx_sdk_cli.cli_password import ( + load_guardian_password, + load_password, + load_relayer_password, +) from multiversx_sdk_cli.constants import ( DEFAULT_TX_VERSION, TRANSACTION_OPTIONS_TX_GUARDED, @@ -69,7 +73,6 @@ def add_tx_args( with_receiver: bool = True, with_data: bool = True, with_estimate_gas: bool = False, - with_relayer_wallet_args: bool = True, ): if with_nonce: sub.add_argument( @@ -121,8 +124,6 @@ def add_tx_args( ) sub.add_argument("--relayer", help="the bech32 address of the relayer") - if with_relayer_wallet_args: - add_relayed_v3_wallet_args(args, sub) add_guardian_args(sub) @@ -360,6 +361,58 @@ def prepare_guardian_account(args: Any): return account +def load_guardian_account(args: Any) -> Union[Account, None]: + if args.guardian_pem: + return Account(pem_file=args.guardian_pem, pem_index=args.guardian_pem_index) + elif args.guardian_keyfile: + password = load_guardian_password(args) + return Account(key_file=args.guardian_keyfile, password=password) + elif args.guardian_ledger: + address = do_get_ledger_address( + account_index=args.guardian_ledger_account_index, + address_index=args.guardian_ledger_address_index, + ) + return Account(Address.new_from_bech32(address)) + + return None + + +def get_guardian_address(guardian: Union[Account, None], args: Any) -> Union[Address, None]: + if guardian: + return guardian.address + + if hasattr(args, "guardian") and args.guardian: + return Address.new_from_bech32(args.guardian) + + return None + + +def get_relayer_address(relayer: Union[Account, None], args: Any) -> Union[Address, None]: + if relayer: + return relayer.address + + if hasattr(args, "relayer") and args.relayer: + return Address.new_from_bech32(args.relayer) + + return None + + +def load_relayer_account(args: Any) -> Union[Account, None]: + if args.relayer_pem: + return Account(pem_file=args.relayer_pem, pem_index=args.relayer_pem_index) + elif args.relayer_keyfile: + password = load_relayer_password(args) + return Account(key_file=args.relayer_keyfile, password=password) + elif args.relayer_ledger: + address = do_get_ledger_address( + account_index=args.relayer_ledger_account_index, + address_index=args.relayer_ledger_address_index, + ) + return Account(Address.new_from_bech32(address)) + + return None + + def prepare_nonce_in_args(args: Any): if args.recall_nonce and not args.proxy: raise ArgumentsNotProvidedError("When using `--recall-nonce`, `--proxy` must be provided, as well") @@ -371,6 +424,15 @@ def prepare_nonce_in_args(args: Any): args.nonce = account.nonce +def get_current_nonce_for_address(address: Address, proxy_url: Union[str, None]) -> int: + if not proxy_url: + raise ArgumentsNotProvidedError("If `--nonce` is not explicitly provided, `--proxy` must be provided") + + network_provider_config = config.get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) + return proxy.get_account(address).nonce + + def prepare_chain_id_in_args(args: Any): if not args.chain and not args.proxy: raise ArgumentsNotProvidedError("chain ID cannot be decided: `--chain` or `--proxy` should be provided") diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 22e6fb04..eeb96eaf 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -33,6 +33,8 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_broadcast_args(sub) cli_shared.add_proxy_arg(sub) cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + sub.add_argument( "--wait-result", action="store_true", @@ -87,6 +89,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_guardian_args(sub) cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) sub.set_defaults(func=sign_transaction) sub = cli_shared.add_command_subparser( diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 0c23e73a..51e7f09c 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -124,6 +124,7 @@ def _add_common_arguments(args: List[str], sub: Any): cli_shared.add_broadcast_args(sub) cli_shared.add_outfile_arg(sub, what="signed transaction, hash") cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) def _add_nodes_arg(sub: Any): diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 42984dee..92ea1c30 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import Any, List, Optional, Protocol, Union +from typing import Any, Optional, Protocol, Union from multiversx_sdk import ( Address, @@ -59,7 +59,7 @@ def prepare_deploy_transaction( self, owner: Account, bytecode: Path, - arguments: Union[List[Any], None], + arguments: Union[list[Any], None], should_prepare_args: bool, upgradeable: bool, readable: bool, @@ -70,7 +70,8 @@ def prepare_deploy_transaction( nonce: int, version: int, options: int, - guardian: Address, + guardian: Union[Address, None], + relayer: Union[Address, None], ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: @@ -91,6 +92,7 @@ def prepare_deploy_transaction( tx.version = version tx.options = options tx.guardian = guardian + tx.relayer = relayer tx.signature = bytes.fromhex(owner.sign_transaction(tx)) return tx @@ -100,15 +102,16 @@ def prepare_execute_transaction( caller: Account, contract: Address, function: str, - arguments: Union[List[Any], None], + arguments: Union[list[Any], None], should_prepare_args: bool, gas_limit: int, value: int, - transfers: Union[List[str], None], + transfers: Union[list[str], None], nonce: int, version: int, options: int, - guardian: Address, + guardian: Union[Address, None], + relayer: Union[Address, None], ) -> Transaction: token_transfers = self._prepare_token_transfers(transfers) if transfers else [] @@ -129,6 +132,7 @@ def prepare_execute_transaction( tx.version = version tx.options = options tx.guardian = guardian + tx.relayer = relayer tx.signature = bytes.fromhex(caller.sign_transaction(tx)) return tx @@ -138,7 +142,7 @@ def prepare_upgrade_transaction( owner: Account, contract: Address, bytecode: Path, - arguments: Union[List[str], None], + arguments: Union[list[str], None], should_prepare_args: bool, upgradeable: bool, readable: bool, @@ -149,7 +153,8 @@ def prepare_upgrade_transaction( nonce: int, version: int, options: int, - guardian: Address, + guardian: Union[Address, None], + relayer: Union[Address, None], ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: @@ -171,6 +176,7 @@ def prepare_upgrade_transaction( tx.version = version tx.options = options tx.guardian = guardian + tx.relayer = relayer tx.signature = bytes.fromhex(owner.sign_transaction(tx)) return tx @@ -180,9 +186,9 @@ def query_contract( contract_address: Address, proxy: INetworkProvider, function: str, - arguments: Optional[List[Any]], + arguments: Optional[list[Any]], should_prepare_args: bool, - ) -> List[Any]: + ) -> list[Any]: args = arguments if arguments else [] if should_prepare_args: args = self._prepare_args_for_factory(args) @@ -196,9 +202,9 @@ def query_contract( return response - def _prepare_token_transfers(self, transfers: List[str]) -> List[TokenTransfer]: + def _prepare_token_transfers(self, transfers: list[str]) -> list[TokenTransfer]: token_computer = TokenComputer() - token_transfers: List[TokenTransfer] = [] + token_transfers: list[TokenTransfer] = [] for i in range(0, len(transfers) - 1, 2): identifier = transfers[i] @@ -211,8 +217,8 @@ def _prepare_token_transfers(self, transfers: List[str]) -> List[TokenTransfer]: return token_transfers - def _prepare_args_for_factory(self, arguments: List[str]) -> List[Any]: - args: List[Any] = [] + def _prepare_args_for_factory(self, arguments: list[str]) -> list[Any]: + args: list[Any] = [] for arg in arguments: if arg.startswith(HEX_PREFIX): @@ -241,7 +247,7 @@ def _hex_to_bytes(self, arg: str): return bytes.fromhex(argument) -def prepare_execute_transaction_data(function: str, arguments: List[Any]) -> str: +def prepare_execute_transaction_data(function: str, arguments: list[Any]) -> str: tx_data = function for arg in arguments: From dfe48d4c9e40317a363db4fb90d023e48afdeca2 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 31 Jan 2025 14:44:53 +0200 Subject: [PATCH 11/84] rename methods --- multiversx_sdk_cli/cli_contracts.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 53f15107..a3260bff 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -378,8 +378,8 @@ def deploy(args: Any): guardian=guardian_address, relayer=relayer_address, ) - tx = _sign_guarded_tx(guardian, args, tx) - _sign_tx_by_relayer(relayer, tx) + tx = _sign_guarded_tx_if_guardian(guardian, args, tx) + _sign_relayed_tx_if_relayer(relayer, tx) address_computer = AddressComputer(NUMBER_OF_SHARDS) contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=tx.nonce) @@ -390,7 +390,7 @@ def deploy(args: Any): _send_or_simulate(tx, contract_address, args) -def _sign_guarded_tx(guardian: Union[IAccount, None], args: Any, tx: Transaction) -> Transaction: +def _sign_guarded_tx_if_guardian(guardian: Union[IAccount, None], args: Any, tx: Transaction) -> Transaction: if guardian: tx.guardian_signature = bytes.fromhex(guardian.sign_transaction(tx)) elif tx.guardian and args.guardian_service_url and args.guardian_2fa_code: @@ -399,7 +399,7 @@ def _sign_guarded_tx(guardian: Union[IAccount, None], args: Any, tx: Transaction return tx -def _sign_tx_by_relayer(relayer: Union[IAccount, None], tx: Transaction): +def _sign_relayed_tx_if_relayer(relayer: Union[IAccount, None], tx: Transaction): if relayer and tx.relayer: tx.relayer_signature = bytes.fromhex(relayer.sign_transaction(tx)) @@ -445,8 +445,8 @@ def call(args: Any): guardian=guardian_address, relayer=relayer_address, ) - tx = _sign_guarded_tx(guardian, args, tx) - _sign_tx_by_relayer(relayer, tx) + tx = _sign_guarded_tx_if_guardian(guardian, args, tx) + _sign_relayed_tx_if_relayer(relayer, tx) _send_or_simulate(tx, contract_address, args) @@ -495,8 +495,8 @@ def upgrade(args: Any): guardian=guardian_address, relayer=relayer_address, ) - tx = _sign_guarded_tx(guardian, args, tx) - _sign_tx_by_relayer(relayer, tx) + tx = _sign_guarded_tx_if_guardian(guardian, args, tx) + _sign_relayed_tx_if_relayer(relayer, tx) _send_or_simulate(tx, contract_address, args) From 9296ee1d9bf8cc62ab85dee45e3a6e34f44b78f3 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 3 Feb 2025 16:10:31 +0200 Subject: [PATCH 12/84] refactor accounts to use the ones from sdk-py --- multiversx_sdk_cli/accounts.py | 142 ----------------- multiversx_sdk_cli/cli_contracts.py | 8 +- multiversx_sdk_cli/cli_output.py | 7 +- multiversx_sdk_cli/cli_shared.py | 147 ++++++++++-------- multiversx_sdk_cli/cli_transactions.py | 6 +- multiversx_sdk_cli/contract_verification.py | 8 +- multiversx_sdk_cli/contracts.py | 14 +- .../delegation/staking_provider.py | 52 +++---- multiversx_sdk_cli/dns.py | 7 +- multiversx_sdk_cli/interfaces.py | 15 +- multiversx_sdk_cli/localnet/genesis_json.py | 3 +- multiversx_sdk_cli/localnet/wallets.py | 9 +- multiversx_sdk_cli/sign_verify.py | 17 +- multiversx_sdk_cli/tests/test_accounts.py | 21 ++- multiversx_sdk_cli/tests/test_contracts.py | 5 +- multiversx_sdk_cli/tests/test_proxy.py | 7 +- multiversx_sdk_cli/transactions.py | 76 +++++---- multiversx_sdk_cli/validators/core.py | 13 +- pyproject.toml | 2 +- requirements.txt | 3 +- 20 files changed, 216 insertions(+), 346 deletions(-) delete mode 100644 multiversx_sdk_cli/accounts.py diff --git a/multiversx_sdk_cli/accounts.py b/multiversx_sdk_cli/accounts.py deleted file mode 100644 index 85baddf6..00000000 --- a/multiversx_sdk_cli/accounts.py +++ /dev/null @@ -1,142 +0,0 @@ -import logging -from pathlib import Path -from typing import Any, Optional, Protocol - -from multiversx_sdk import ( - AccountOnNetwork, - Address, - Message, - MessageComputer, - Transaction, - TransactionComputer, - UserSigner, -) - -from multiversx_sdk_cli.config import get_address_hrp -from multiversx_sdk_cli.interfaces import IAccount -from multiversx_sdk_cli.ledger.config import compare_versions -from multiversx_sdk_cli.ledger.ledger_app_handler import SIGN_USING_HASH_VERSION -from multiversx_sdk_cli.ledger.ledger_functions import ( - TX_HASH_SIGN_OPTIONS, - TX_HASH_SIGN_VERSION, - do_get_ledger_address, - do_get_ledger_version, - do_sign_message_with_ledger, - do_sign_transaction_with_ledger, -) - -logger = logging.getLogger("accounts") - - -# fmt: off -class INetworkProvider(Protocol): - def get_account(self, address: Address) -> AccountOnNetwork: - ... -# fmt: on - - -class AccountBase(IAccount): - def __init__(self, address: Any = Address.empty()) -> None: - self.address = address - self.nonce: int = 0 - - def sync_nonce(self, proxy: INetworkProvider): - logger.debug("AccountBase.sync_nonce()") - self.nonce = proxy.get_account(self.address).nonce - logger.debug(f"AccountBase.sync_nonce() done: {self.nonce}") - - def sign_transaction(self, transaction: Transaction) -> str: - raise NotImplementedError - - def sign_message(self, data: bytes) -> str: - raise NotImplementedError - - -class Account(AccountBase): - def __init__( - self, - address: Any = None, - pem_file: Optional[str] = None, - pem_index: int = 0, - key_file: str = "", - password: str = "", - ) -> None: - super().__init__(address) - - if pem_file: - pem_path = Path(pem_file).expanduser().resolve() - self.signer = UserSigner.from_pem_file(pem_path, pem_index) - self.address = Address(self.signer.get_pubkey().buffer, get_address_hrp()) - elif key_file and password: - key_file_path = Path(key_file).expanduser().resolve() - self.signer = UserSigner.from_wallet(key_file_path, password) - self.address = Address(self.signer.get_pubkey().buffer, get_address_hrp()) - - def sign_transaction(self, transaction: Transaction) -> str: - assert self.signer is not None - - transaction_computer = TransactionComputer() - if transaction.options & TX_HASH_SIGN_OPTIONS == TX_HASH_SIGN_OPTIONS: - return self.signer.sign(transaction_computer.compute_hash_for_signing(transaction)).hex() - - return self.signer.sign(transaction_computer.compute_bytes_for_signing(transaction)).hex() - - def sign_message(self, data: bytes) -> str: - assert self.signer is not None - message = Message(data) - message_computer = MessageComputer() - signature = self.signer.sign(message_computer.compute_bytes_for_signing(message)) - - logger.debug( - f"Account.sign_message(): raw_data_to_sign = {data.hex()}, message_data_to_sign = {message_computer.compute_bytes_for_signing(message).hex()}, signature = {signature.hex()}" - ) - return signature.hex() - - -class LedgerAccount(Account): - def __init__(self, account_index: int = 0, address_index: int = 0) -> None: - super().__init__() - self.account_index = account_index - self.address_index = address_index - self.address = Address.new_from_bech32( - do_get_ledger_address(account_index=account_index, address_index=address_index) - ) - - def sign_transaction(self, transaction: Transaction) -> str: - ledger_version = do_get_ledger_version() - should_use_hash_signing = compare_versions(ledger_version, SIGN_USING_HASH_VERSION) >= 0 - - # TODO: This check will be removed in the next major release. - if should_use_hash_signing: - transaction.version = TX_HASH_SIGN_VERSION - transaction.options = transaction.options | TX_HASH_SIGN_OPTIONS - - transaction_computer = TransactionComputer() - - signature = do_sign_transaction_with_ledger( - transaction_computer.compute_bytes_for_signing(transaction), - account_index=self.account_index, - address_index=self.address_index, - sign_using_hash=should_use_hash_signing, - ) - - assert isinstance(signature, str) - return signature - - def sign_message(self, data: bytes) -> str: - message_length = len(data).to_bytes(4, byteorder="big") - message_data_to_sign: bytes = message_length + data - logger.debug( - f"LedgerAccount.sign_message(): raw_data_to_sign = {data.hex()}, message_data_to_sign = {message_data_to_sign.hex()}" - ) - - signature = do_sign_message_with_ledger( - message_data_to_sign, - account_index=self.account_index, - address_index=self.address_index, - ) - - assert isinstance(signature, str) - - logger.debug(f"LedgerAccount.sign_message(): signature = {signature}") - return signature diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index a3260bff..af04f617 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -2,9 +2,10 @@ import logging import os from pathlib import Path -from typing import Any, Union +from typing import Any, Union, cast from multiversx_sdk import ( + Account, Address, AddressComputer, ProxyNetworkProvider, @@ -392,7 +393,7 @@ def deploy(args: Any): def _sign_guarded_tx_if_guardian(guardian: Union[IAccount, None], args: Any, tx: Transaction) -> Transaction: if guardian: - tx.guardian_signature = bytes.fromhex(guardian.sign_transaction(tx)) + tx.guardian_signature = guardian.sign_transaction(tx) elif tx.guardian and args.guardian_service_url and args.guardian_2fa_code: tx = cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) @@ -401,7 +402,7 @@ def _sign_guarded_tx_if_guardian(guardian: Union[IAccount, None], args: Any, tx: def _sign_relayed_tx_if_relayer(relayer: Union[IAccount, None], tx: Transaction): if relayer and tx.relayer: - tx.relayer_signature = bytes.fromhex(relayer.sign_transaction(tx)) + tx.relayer_signature = relayer.sign_transaction(tx) def call(args: Any): @@ -555,6 +556,7 @@ def verify(args: Any) -> None: packaged_src = Path(args.packaged_src).expanduser().resolve() owner = cli_shared.prepare_account(args) + owner = cast(Account, owner) docker_image = args.docker_image contract_variant = args.contract_variant diff --git a/multiversx_sdk_cli/cli_output.py b/multiversx_sdk_cli/cli_output.py index ab0c30aa..eed1e76a 100644 --- a/multiversx_sdk_cli/cli_output.py +++ b/multiversx_sdk_cli/cli_output.py @@ -3,10 +3,9 @@ from collections import OrderedDict from typing import Any, Dict, List, Optional, Union -from multiversx_sdk import Transaction, TransactionOnNetwork +from multiversx_sdk import Address, Transaction, TransactionOnNetwork from multiversx_sdk_cli import utils -from multiversx_sdk_cli.interfaces import IAddress from multiversx_sdk_cli.utils import ISerializable logger = logging.getLogger("cli.output") @@ -17,7 +16,7 @@ def __init__(self) -> None: self.emitted_transaction_hash: Optional[str] = None self.emitted_transaction: Union[Transaction, None] = None self.emitted_transaction_omitted_fields: List[str] = [] - self.contract_address: Union[IAddress, None] = None + self.contract_address: Union[Address, None] = None self.transaction_on_network: Union[TransactionOnNetwork, None] = None self.transaction_on_network_omitted_fields: List[str] = [] self.simulation_results: Union[ISerializable, None] = None @@ -31,7 +30,7 @@ def set_emitted_transaction(self, emitted_transaction: Transaction, omitted_fiel self.emitted_transaction_omitted_fields = omitted_fields return self - def set_contract_address(self, contract_address: IAddress): + def set_contract_address(self, contract_address: Address): self.contract_address = contract_address return self diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 67f77c7d..37072185 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -3,12 +3,18 @@ import copy import sys from argparse import FileType +from pathlib import Path from typing import Any, Dict, List, Text, Union, cast -from multiversx_sdk import Address, ProxyNetworkProvider, Transaction +from multiversx_sdk import ( + Account, + Address, + LedgerAccount, + ProxyNetworkProvider, + Transaction, +) from multiversx_sdk_cli import config, errors, utils -from multiversx_sdk_cli.accounts import Account, LedgerAccount from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.cli_password import ( load_guardian_password, @@ -20,7 +26,7 @@ TRANSACTION_OPTIONS_TX_GUARDED, ) from multiversx_sdk_cli.errors import ArgumentsNotProvidedError -from multiversx_sdk_cli.ledger.ledger_functions import do_get_ledger_address +from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.simulation import Simulator from multiversx_sdk_cli.transactions import send_and_wait_for_result from multiversx_sdk_cli.utils import log_explorer_transaction @@ -167,6 +173,12 @@ def add_wallet_args(args: List[str], sub: Any): "--passfile", help="🔑 a file containing keyfile's password, if keyfile provided", ) + sub.add_argument( + "--address-index", + type=int, + default=None, + help="🔑 the index of the address in the keyfile; should only be provided for keyfile of kind = mnemonic", + ) sub.add_argument( "--ledger", action="store_true", @@ -174,12 +186,6 @@ def add_wallet_args(args: List[str], sub: Any): default=False, help="🔐 bool flag for signing transaction using ledger", ) - sub.add_argument( - "--ledger-account-index", - type=int, - default=0, - help="🔐 the index of the account when using Ledger", - ) sub.add_argument( "--ledger-address-index", type=int, @@ -210,6 +216,12 @@ def add_guardian_wallet_args(args: List[str], sub: Any): "--guardian-passfile", help="🔑 a file containing keyfile's password, if keyfile provided", ) + sub.add_argument( + "--guardian-address-index", + type=int, + default=None, + help="🔑 the index of the address in the keyfile; should only be provided for keyfile of kind = mnemonic", + ) sub.add_argument( "--guardian-ledger", action="store_true", @@ -217,12 +229,6 @@ def add_guardian_wallet_args(args: List[str], sub: Any): default=False, help="🔐 bool flag for signing transaction using ledger", ) - sub.add_argument( - "--guardian-ledger-account-index", - type=int, - default=0, - help="🔐 the index of the account when using Ledger", - ) sub.add_argument( "--guardian-ledger-address-index", type=int, @@ -245,18 +251,18 @@ def add_relayed_v3_wallet_args(args: List[str], sub: Any): "--relayer-passfile", help="🔑 a file containing keyfile's password, if keyfile provided", ) + sub.add_argument( + "--relayer-address-index", + type=int, + default=None, + help="🔑 the index of the address in the keyfile; should only be provided for keyfile of kind = mnemonic", + ) sub.add_argument( "--relayer-ledger", action="store_true", default=False, help="🔐 bool flag for signing transaction using ledger", ) - sub.add_argument( - "--relayer-ledger-account-index", - type=int, - default=0, - help="🔐 the index of the account when using Ledger", - ) sub.add_argument( "--relayer-ledger-address-index", type=int, @@ -310,74 +316,88 @@ def parse_omit_fields_arg(args: Any) -> List[str]: def prepare_account(args: Any): + hrp = config.get_address_hrp() + if args.pem: - account = Account(pem_file=args.pem, pem_index=args.pem_index) + return Account.new_from_pem(file_path=Path(args.pem), index=args.pem_index, hrp=hrp) elif args.keyfile: password = load_password(args) - account = Account(key_file=args.keyfile, password=password) - elif args.ledger: - account = LedgerAccount( - account_index=args.ledger_account_index, - address_index=args.ledger_address_index, + account = Account.new_from_keystore( + file_path=Path(args.keyfile), + password=password, + address_index=args.address_index, + hrp=hrp, ) + elif args.ledger: + account = LedgerAccount(address_index=args.ledger_address_index) else: raise errors.NoWalletProvided() return account -def prepare_relayer_account(args: Any) -> Account: +def prepare_relayer_account(args: Any) -> IAccount: + hrp = config.get_address_hrp() + if args.relayer_ledger: - account = LedgerAccount( - account_index=args.relayer_ledger_account_index, - address_index=args.relayer_ledger_address_index, - ) + account = LedgerAccount(address_index=args.relayer_ledger_address_index) if args.relayer_pem: - account = Account(pem_file=args.relayer_pem, pem_index=args.relayer_pem_index) + account = Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_pem_index, hrp=hrp) elif args.relayer_keyfile: password = load_password(args) - account = Account(key_file=args.relayer_keyfile, password=password) + account = Account.new_from_keystore( + file_path=Path(args.relayer_keyfile), + password=password, + address_index=args.relayer_address_index, + hrp=hrp, + ) else: raise errors.NoWalletProvided() return account -def prepare_guardian_account(args: Any): +def prepare_guardian_account(args: Any) -> IAccount: + hrp = config.get_address_hrp() + if args.guardian_pem: - account = Account(pem_file=args.guardian_pem, pem_index=args.guardian_pem_index) + account = Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_pem_index, hrp=hrp) elif args.guardian_keyfile: password = load_guardian_password(args) - account = Account(key_file=args.guardian_keyfile, password=password) - elif args.guardian_ledger: - address = do_get_ledger_address( - account_index=args.guardian_ledger_account_index, - address_index=args.guardian_ledger_address_index, + account = Account.new_from_keystore( + file_path=Path(args.guardian_keyfile), + password=password, + address_index=args.guardian_address_index, + hrp=hrp, ) - account = Account(Address.new_from_bech32(address)) + elif args.guardian_ledger: + account = LedgerAccount(address_index=args.relayer_ledger_address_index) else: raise errors.NoWalletProvided() return account -def load_guardian_account(args: Any) -> Union[Account, None]: +def load_guardian_account(args: Any) -> Union[IAccount, None]: + hrp = config.get_address_hrp() + if args.guardian_pem: - return Account(pem_file=args.guardian_pem, pem_index=args.guardian_pem_index) + return Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_pem_index, hrp=hrp) elif args.guardian_keyfile: password = load_guardian_password(args) - return Account(key_file=args.guardian_keyfile, password=password) - elif args.guardian_ledger: - address = do_get_ledger_address( - account_index=args.guardian_ledger_account_index, - address_index=args.guardian_ledger_address_index, + return Account.new_from_keystore( + file_path=Path(args.guardian_keyfile), + password=password, + address_index=args.guardian_address_index, + hrp=hrp, ) - return Account(Address.new_from_bech32(address)) + elif args.guardian_ledger: + return LedgerAccount(address_index=args.guardian_ledger_address_index) return None -def get_guardian_address(guardian: Union[Account, None], args: Any) -> Union[Address, None]: +def get_guardian_address(guardian: Union[IAccount, None], args: Any) -> Union[Address, None]: if guardian: return guardian.address @@ -387,7 +407,7 @@ def get_guardian_address(guardian: Union[Account, None], args: Any) -> Union[Add return None -def get_relayer_address(relayer: Union[Account, None], args: Any) -> Union[Address, None]: +def get_relayer_address(relayer: Union[IAccount, None], args: Any) -> Union[Address, None]: if relayer: return relayer.address @@ -397,18 +417,21 @@ def get_relayer_address(relayer: Union[Account, None], args: Any) -> Union[Addre return None -def load_relayer_account(args: Any) -> Union[Account, None]: +def load_relayer_account(args: Any) -> Union[IAccount, None]: + hrp = config.get_address_hrp() + if args.relayer_pem: - return Account(pem_file=args.relayer_pem, pem_index=args.relayer_pem_index) + return Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_pem_index, hrp=hrp) elif args.relayer_keyfile: password = load_relayer_password(args) - return Account(key_file=args.relayer_keyfile, password=password) - elif args.relayer_ledger: - address = do_get_ledger_address( - account_index=args.relayer_ledger_account_index, - address_index=args.relayer_ledger_address_index, + return Account.new_from_keystore( + file_path=Path(args.relayer_keyfile), + password=password, + address_index=args.relayer_address_index, + hrp=hrp, ) - return Account(Address.new_from_bech32(address)) + elif args.relayer_ledger: + return LedgerAccount(address_index=args.relayer_ledger_address_index) return None @@ -420,8 +443,8 @@ def prepare_nonce_in_args(args: Any): if args.recall_nonce: account = prepare_account(args) network_provider_config = config.get_config_for_network_providers() - account.sync_nonce(ProxyNetworkProvider(url=args.proxy, config=network_provider_config)) - args.nonce = account.nonce + proxy = ProxyNetworkProvider(url=args.proxy, config=network_provider_config) + args.nonce = proxy.get_account(account.address).nonce def get_current_nonce_for_address(address: Address, proxy_url: Union[str, None]) -> int: diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index eeb96eaf..6feb32e5 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -171,7 +171,7 @@ def sign_transaction(args: Any): cli_shared.check_options_for_guarded_tx(tx.options) account = cli_shared.prepare_account(args) - tx.signature = bytes.fromhex(account.sign_transaction(tx)) + tx.signature = account.sign_transaction(tx) try: guardian_account = cli_shared.prepare_guardian_account(args) @@ -179,7 +179,7 @@ def sign_transaction(args: Any): guardian_account = None if guardian_account: - tx.guardian_signature = bytes.fromhex(guardian_account.sign_transaction(tx)) + tx.guardian_signature = guardian_account.sign_transaction(tx) elif args.guardian: tx = cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) @@ -200,7 +200,7 @@ def relay_transaction(args: Any): if tx.relayer != relayer.address: raise IncorrectWalletError("Relayer wallet does not match the relayer's address set in the transaction.") - tx.relayer_signature = bytes.fromhex(relayer.sign_transaction(tx)) + tx.relayer_signature = relayer.sign_transaction(tx) cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/contract_verification.py b/multiversx_sdk_cli/contract_verification.py index 1b176668..561e3828 100644 --- a/multiversx_sdk_cli/contract_verification.py +++ b/multiversx_sdk_cli/contract_verification.py @@ -6,9 +6,8 @@ from typing import Any, Dict, Optional, Tuple import requests -from multiversx_sdk import Address +from multiversx_sdk import Account, Address, Message -from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.errors import KnownError from multiversx_sdk_cli.utils import dump_out_json, read_json_file @@ -114,10 +113,7 @@ def _create_request_signature(account: Account, contract_address: Address, reque hashed_payload: str = hashlib.sha256(request_payload).hexdigest() raw_data_to_sign = f"{contract_address.to_bech32()}{hashed_payload}" - signature_hex = account.sign_message(raw_data_to_sign.encode()) - signature = bytes.fromhex(signature_hex) - - return signature + return account.sign_message(Message(raw_data_to_sign.encode())) def query_status_with_task_id(url: str, task_id: str, interval: int = 10): diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 92ea1c30..add4b855 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -26,8 +26,8 @@ ) from multiversx_sdk_cli import errors -from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.config import get_address_hrp +from multiversx_sdk_cli.interfaces import IAccount logger = logging.getLogger("contracts") @@ -57,7 +57,7 @@ def __init__(self, config: TransactionsFactoryConfig, abi: Optional[Abi] = None) def prepare_deploy_transaction( self, - owner: Account, + owner: IAccount, bytecode: Path, arguments: Union[list[Any], None], should_prepare_args: bool, @@ -93,13 +93,13 @@ def prepare_deploy_transaction( tx.options = options tx.guardian = guardian tx.relayer = relayer - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx def prepare_execute_transaction( self, - caller: Account, + caller: IAccount, contract: Address, function: str, arguments: Union[list[Any], None], @@ -133,13 +133,13 @@ def prepare_execute_transaction( tx.options = options tx.guardian = guardian tx.relayer = relayer - tx.signature = bytes.fromhex(caller.sign_transaction(tx)) + tx.signature = caller.sign_transaction(tx) return tx def prepare_upgrade_transaction( self, - owner: Account, + owner: IAccount, contract: Address, bytecode: Path, arguments: Union[list[str], None], @@ -177,7 +177,7 @@ def prepare_upgrade_transaction( tx.options = options tx.guardian = guardian tx.relayer = relayer - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation/staking_provider.py index 82453d63..3f48585e 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Any, List, Protocol, Tuple +from typing import Any, List, Tuple from multiversx_sdk import ( Address, @@ -12,22 +12,10 @@ from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.errors import BadUsage +from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.validators.validators_file import ValidatorsFile -# fmt: off -class IAccount(Protocol): - @property - def address(self) -> Address: - ... - - nonce: int - - def sign_transaction(self, transaction: Transaction) -> str: - ... -# fmt: on - - class DelegationOperations: def __init__(self, config: TransactionsFactoryConfig) -> None: self._factory = DelegationTransactionsFactory(config) @@ -47,7 +35,7 @@ def prepare_transaction_for_new_delegation_contract(self, owner: IAccount, args: if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -69,7 +57,7 @@ def prepare_transaction_for_adding_nodes(self, owner: IAccount, args: Any) -> Tr if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -91,7 +79,7 @@ def prepare_transaction_for_removing_nodes(self, owner: IAccount, args: Any) -> if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -113,7 +101,7 @@ def prepare_transaction_for_staking_nodes(self, owner: IAccount, args: Any) -> T if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -135,7 +123,7 @@ def prepare_transaction_for_unbonding_nodes(self, owner: IAccount, args: Any) -> if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -157,7 +145,7 @@ def prepare_transaction_for_unstaking_nodes(self, owner: IAccount, args: Any) -> if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -182,7 +170,7 @@ def prepare_transaction_for_unjailing_nodes(self, owner: IAccount, args: Any) -> if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -202,7 +190,7 @@ def prepare_transaction_for_delegating(self, owner: IAccount, args: Any) -> Tran if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -220,7 +208,7 @@ def prepare_transaction_for_claiming_rewards(self, owner: IAccount, args: Any) - if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -238,7 +226,7 @@ def prepare_transaction_for_redelegating_rewards(self, owner: IAccount, args: An if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -258,7 +246,7 @@ def prepare_transaction_for_undelegating(self, owner: IAccount, args: Any) -> Tr if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -276,7 +264,7 @@ def prepare_transaction_for_withdrawing(self, owner: IAccount, args: Any) -> Tra if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -296,7 +284,7 @@ def prepare_transaction_for_changing_service_fee(self, owner: IAccount, args: An if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -316,7 +304,7 @@ def prepare_transaction_for_modifying_delegation_cap(self, owner: IAccount, args if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -342,7 +330,7 @@ def prepare_transaction_for_automatic_activation(self, owner: IAccount, args: An if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -368,7 +356,7 @@ def prepare_transaction_for_redelegate_cap(self, owner: IAccount, args: Any) -> if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -390,7 +378,7 @@ def prepare_transaction_for_setting_metadata(self, owner: IAccount, args: Any) - if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx @@ -422,7 +410,7 @@ def prepare_transaction_for_creating_delegation_contract_from_validator( if args.gas_limit: tx.gas_limit = int(args.gas_limit) - tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + tx.signature = owner.sign_transaction(tx) return tx diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 8e277e11..3bacd1b4 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -11,7 +11,6 @@ ) from multiversx_sdk_cli import cli_shared, utils -from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX from multiversx_sdk_cli.contracts import SmartContract @@ -136,10 +135,10 @@ def compute_dns_address_for_shard_id(shard_id: int) -> Address: deployer_pubkey_prefix = InitialDNSAddress[: len(InitialDNSAddress) - ShardIdentiferLen] deployer_pubkey = deployer_pubkey_prefix + bytes([0, shard_id]) - deployer = Account(address=Address(deployer_pubkey, get_address_hrp())) - deployer.nonce = 0 + deployer = Address(deployer_pubkey, get_address_hrp()) + nonce = 0 address_computer = AddressComputer(number_of_shards=3) - contract_address = address_computer.compute_contract_address(deployer.address, deployer.nonce) + contract_address = address_computer.compute_contract_address(deployer, nonce) return contract_address diff --git a/multiversx_sdk_cli/interfaces.py b/multiversx_sdk_cli/interfaces.py index 0c98b6f2..67066828 100644 --- a/multiversx_sdk_cli/interfaces.py +++ b/multiversx_sdk_cli/interfaces.py @@ -1,17 +1,9 @@ from typing import Any, Dict, Protocol -from multiversx_sdk import Transaction +from multiversx_sdk import Address, Transaction # fmt: off -class IAddress(Protocol): - def to_hex(self) -> str: - ... - - def to_bech32(self) -> str: - ... - - class ITransaction(Protocol): sender: str receiver: str @@ -33,7 +25,10 @@ class ITransaction(Protocol): class IAccount(Protocol): - def sign_transaction(self, transaction: Transaction) -> str: + use_hash_signing: bool + address: Address + + def sign_transaction(self, transaction: Transaction) -> bytes: ... diff --git a/multiversx_sdk_cli/localnet/genesis_json.py b/multiversx_sdk_cli/localnet/genesis_json.py index 0b802ed8..f7fd3d61 100644 --- a/multiversx_sdk_cli/localnet/genesis_json.py +++ b/multiversx_sdk_cli/localnet/genesis_json.py @@ -1,6 +1,7 @@ from typing import Any, Dict, List -from multiversx_sdk_cli.accounts import Account +from multiversx_sdk import Account + from multiversx_sdk_cli.localnet import wallets from multiversx_sdk_cli.localnet.config_root import ConfigRoot from multiversx_sdk_cli.localnet.genesis import is_last_user diff --git a/multiversx_sdk_cli/localnet/wallets.py b/multiversx_sdk_cli/localnet/wallets.py index f4e3e5e2..a68c2a44 100644 --- a/multiversx_sdk_cli/localnet/wallets.py +++ b/multiversx_sdk_cli/localnet/wallets.py @@ -3,10 +3,9 @@ from pathlib import Path from typing import Dict, Tuple -from multiversx_sdk import ValidatorPEM +from multiversx_sdk import Account, ValidatorPEM from multiversx_sdk_cli import errors, utils -from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.workstation import get_tools_folder MAX_NUM_NODES = 12 @@ -36,7 +35,7 @@ def get_validator_wallets(num_validators: int) -> Dict[str, Account]: for i in range(0, num_validators): pem_file = get_validator_wallet_file(i) nickname = "validator{:02}".format(i) - account = Account(pem_file=str(pem_file)) + account = Account.new_from_pem(file_path=pem_file) result[nickname] = account return result @@ -52,7 +51,7 @@ def get_validators(num_validators: int) -> Dict[str, Tuple[str, Account]]: pem_file = get_validator_wallet_file(i) nickname = "validator{:02}".format(i) - account = Account(pem_file=str(pem_file)) + account = Account.new_from_pem(file_path=pem_file) result[nickname] = (pubkey, account) return result @@ -81,7 +80,7 @@ def get_users() -> Dict[str, Account]: for pem_file in sorted(utils.list_files(_get_users_folder(), ".pem")): nickname = Path(pem_file).stem - account = Account(pem_file=str(pem_file)) + account = Account.new_from_pem(file_path=pem_file) result[nickname] = account return result diff --git a/multiversx_sdk_cli/sign_verify.py b/multiversx_sdk_cli/sign_verify.py index 27903f50..68724e00 100644 --- a/multiversx_sdk_cli/sign_verify.py +++ b/multiversx_sdk_cli/sign_verify.py @@ -1,8 +1,15 @@ -from typing import Dict +from typing import Dict, Protocol from multiversx_sdk import Address, Message, MessageComputer, UserVerifier -from multiversx_sdk_cli.accounts import Account + +# fmt: off +class IAccount(Protocol): + address: Address + + def sign_message(self, message: Message) -> bytes: + ... +# fmt: off class SignedMessage: @@ -38,6 +45,6 @@ def to_dictionary(self) -> Dict[str, str]: } -def sign_message(message: str, account: Account) -> SignedMessage: - signature = account.sign_message(message.encode()) - return SignedMessage(account.address.to_bech32(), message, signature) +def sign_message(message: str, account: IAccount) -> SignedMessage: + signature = account.sign_message(Message(message.encode())) + return SignedMessage(account.address.to_bech32(), message, signature.hex()) diff --git a/multiversx_sdk_cli/tests/test_accounts.py b/multiversx_sdk_cli/tests/test_accounts.py index e89e20ec..8272763f 100644 --- a/multiversx_sdk_cli/tests/test_accounts.py +++ b/multiversx_sdk_cli/tests/test_accounts.py @@ -1,32 +1,31 @@ from pathlib import Path import pytest - -from multiversx_sdk_cli.accounts import Account +from multiversx_sdk import Account def test_load_account_from_keystore_without_kind(): alice_json = Path(__file__).parent / "testdata" / "alice.json" - account = Account(key_file=str(alice_json), password="password") - assert account.address.bech32() == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + account = Account.new_from_keystore(file_path=alice_json, password="password") + assert account.address.to_bech32() == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" with pytest.raises(Exception): - _ = Account(key_file=str(alice_json), password="wrong_password") + _ = Account.new_from_keystore(file_path=alice_json, password="wrong_password") def test_load_account_from_keystore_with_kind_secret_key(): keystore_path = Path(__file__).parent / "testdata" / "aliceWithKindSecretKey.json" - account = Account(key_file=str(keystore_path), password="password") - assert account.address.bech32() == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + account = Account.new_from_keystore(file_path=keystore_path, password="password") + assert account.address.to_bech32() == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" with pytest.raises(Exception): - _ = Account(key_file=str(keystore_path), password="wrong_password") + _ = Account.new_from_keystore(file_path=keystore_path, password="wrong_password") def test_load_account_from_keystore_with_kind_mnemonic(): keystore_path = Path(__file__).parent / "testdata" / "withDummyMnemonic.json" - account = Account(key_file=str(keystore_path), password="password") - assert account.address.bech32() == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + account = Account.new_from_keystore(file_path=keystore_path, password="password") + assert account.address.to_bech32() == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" with pytest.raises(Exception): - _ = Account(key_file=str(keystore_path), password="wrong_password") + _ = Account.new_from_keystore(file_path=keystore_path, password="wrong_password") diff --git a/multiversx_sdk_cli/tests/test_contracts.py b/multiversx_sdk_cli/tests/test_contracts.py index 5e70c90a..ef312959 100644 --- a/multiversx_sdk_cli/tests/test_contracts.py +++ b/multiversx_sdk_cli/tests/test_contracts.py @@ -3,10 +3,9 @@ import pytest from Cryptodome.Hash import keccak -from multiversx_sdk import Address, TransactionsFactoryConfig +from multiversx_sdk import Account, Address, TransactionsFactoryConfig from multiversx_sdk_cli import errors -from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.contract_verification import _create_request_signature from multiversx_sdk_cli.contracts import SmartContract, _prepare_argument @@ -54,7 +53,7 @@ def test_prepare_argument(): def test_contract_verification_create_request_signature(): - account = Account(pem_file=str(testdata_folder / "walletKey.pem")) + account = Account.new_from_pem(file_path=testdata_folder / "walletKey.pem") contract_address = Address.from_bech32("erd1qqqqqqqqqqqqqpgqeyj9g344pqguukajpcfqz9p0rfqgyg4l396qespdck") request_payload = b"test" signature = _create_request_signature(account, contract_address, request_payload) diff --git a/multiversx_sdk_cli/tests/test_proxy.py b/multiversx_sdk_cli/tests/test_proxy.py index 947af319..45efb5e7 100644 --- a/multiversx_sdk_cli/tests/test_proxy.py +++ b/multiversx_sdk_cli/tests/test_proxy.py @@ -1,6 +1,5 @@ from multiversx_sdk import Address, ProxyNetworkProvider -from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.cli import main from multiversx_sdk_cli.config import get_config_for_network_providers @@ -20,11 +19,11 @@ def test_get_account(): def test_sync_nonce(): - account = Account(address=Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")) + account = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") config = get_config_for_network_providers() proxy = ProxyNetworkProvider("https://testnet-api.multiversx.com", config=config) - account.sync_nonce(proxy) - assert True if account.nonce else False + nonce = proxy.get_account(account).nonce + assert True if nonce else False def test_query_contract(): diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index e23206b1..402fa04a 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -1,10 +1,13 @@ import json import logging import time +from pathlib import Path from typing import Any, List, Protocol, TextIO, Union from multiversx_sdk import ( + Account, Address, + LedgerAccount, Token, TokenComputer, TokenTransfer, @@ -15,12 +18,11 @@ TransferTransactionsFactory, ) -from multiversx_sdk_cli import errors -from multiversx_sdk_cli.accounts import Account, AccountBase, LedgerAccount +from multiversx_sdk_cli import config, errors from multiversx_sdk_cli.cli_password import load_guardian_password, load_password from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.errors import IncorrectWalletError, NoWalletProvided -from multiversx_sdk_cli.ledger.ledger_functions import do_get_ledger_address +from multiversx_sdk_cli.interfaces import IAccount logger = logging.getLogger("transactions") @@ -91,7 +93,7 @@ def do_prepare_transaction(args: Any) -> Transaction: if isinstance(relayer_account, LedgerAccount): tx_computer.apply_options_for_hash_signing(tx) - tx.relayer_signature = bytes.fromhex(relayer_account.sign_transaction(tx)) + tx.relayer_signature = relayer_account.sign_transaction(tx) except NoWalletProvided: logger.warning("Relayer wallet not provided. Transaction will not be signed by relayer.") except IncorrectWalletError: @@ -105,39 +107,48 @@ def do_prepare_transaction(args: Any) -> Transaction: except NoWalletProvided: guardian_account = None - tx.signature = bytes.fromhex(account.sign_transaction(tx)) + tx.signature = account.sign_transaction(tx) tx = sign_tx_by_guardian(args, tx, guardian_account) return tx -def load_sender_account_from_args(args: Any) -> Account: - account = Account() - if args.ledger: - account = LedgerAccount( - account_index=args.ledger_account_index, - address_index=args.ledger_address_index, - ) +def load_sender_account_from_args(args: Any) -> IAccount: + hrp = config.get_address_hrp() + if args.pem: - account = Account(pem_file=args.pem, pem_index=args.pem_index) + return Account.new_from_pem(file_path=Path(args.pem), index=args.pem_index, hrp=hrp) elif args.keyfile: password = load_password(args) - account = Account(key_file=args.keyfile, password=password) + account = Account.new_from_keystore( + file_path=Path(args.keyfile), + password=password, + address_index=args.address_index, + hrp=hrp, + ) + elif args.ledger: + account = LedgerAccount(address_index=args.ledger_address_index) + else: + raise errors.NoWalletProvided() return account -def load_relayer_account_from_args(args: Any) -> Account: +def load_relayer_account_from_args(args: Any) -> IAccount: + hrp = config.get_address_hrp() + if args.relayer_ledger: - account = LedgerAccount( - account_index=args.relayer_ledger_account_index, - address_index=args.relayer_ledger_address_index, - ) + account = LedgerAccount(address_index=args.relayer_ledger_address_index) if args.relayer_pem: - account = Account(pem_file=args.relayer_pem, pem_index=args.relayer_pem_index) + account = Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_pem_index, hrp=hrp) elif args.relayer_keyfile: password = load_password(args) - account = Account(key_file=args.relayer_keyfile, password=password) + account = Account.new_from_keystore( + file_path=Path(args.relayer_keyfile), + password=password, + address_index=args.relayer_address_index, + hrp=hrp, + ) else: raise errors.NoWalletProvided() @@ -160,9 +171,9 @@ def prepare_token_transfers(transfers: List[Any]) -> List[TokenTransfer]: return token_transfers -def sign_tx_by_guardian(args: Any, tx: Transaction, guardian_account: Union[AccountBase, None]) -> Transaction: +def sign_tx_by_guardian(args: Any, tx: Transaction, guardian_account: Union[IAccount, None]) -> Transaction: if guardian_account: - tx.guardian_signature = bytes.fromhex(guardian_account.sign_transaction(tx)) + tx.guardian_signature = guardian_account.sign_transaction(tx) elif args.guardian: tx = cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) # type: ignore @@ -170,18 +181,21 @@ def sign_tx_by_guardian(args: Any, tx: Transaction, guardian_account: Union[Acco # TODO: this is duplicated code; a proper refactoring will come later -def get_guardian_account_from_args(args: Any): +def get_guardian_account_from_args(args: Any) -> IAccount: + hrp = config.get_address_hrp() + if args.guardian_pem: - account = Account(pem_file=args.guardian_pem, pem_index=args.guardian_pem_index) + account = Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_pem_index, hrp=hrp) elif args.guardian_keyfile: password = load_guardian_password(args) - account = Account(key_file=args.guardian_keyfile, password=password) - elif args.guardian_ledger: - address = do_get_ledger_address( - account_index=args.guardian_ledger_account_index, - address_index=args.guardian_ledger_address_index, + account = Account.new_from_keystore( + file_path=Path(args.guardian_keyfile), + password=password, + address_index=args.guardian_address_index, + hrp=hrp, ) - account = Account(address=Address.new_from_bech32(address)) + elif args.guardian_ledger: + account = LedgerAccount(address_index=args.relayer_ledger_address_index) else: raise errors.NoWalletProvided() diff --git a/multiversx_sdk_cli/validators/core.py b/multiversx_sdk_cli/validators/core.py index 74269e7b..5a6bbd2a 100644 --- a/multiversx_sdk_cli/validators/core.py +++ b/multiversx_sdk_cli/validators/core.py @@ -4,9 +4,7 @@ from multiversx_sdk import Address, ValidatorPEM, ValidatorSigner -from multiversx_sdk_cli import utils -from multiversx_sdk_cli.accounts import Account -from multiversx_sdk_cli.cli_password import load_password +from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.config import ( GAS_PER_DATA_BYTE, MIN_GAS_LIMIT, @@ -14,7 +12,6 @@ get_address_hrp, ) from multiversx_sdk_cli.contracts import prepare_execute_transaction_data -from multiversx_sdk_cli.errors import BadUsage from multiversx_sdk_cli.validators.validators_file import ValidatorsFile logger = logging.getLogger("validators") @@ -27,13 +24,7 @@ def prepare_args_for_stake(args: Any): prepare_args_for_top_up(args) return - if args.pem: - node_operator = Account(pem_file=args.pem) - elif args.keyfile: - password = load_password(args) - node_operator = Account(key_file=args.keyfile, password=password) - else: - raise BadUsage("cannot initialize node operator") + node_operator = cli_shared.prepare_account(args) validators_file_path = Path(args.validators_file) reward_address = Address.new_from_bech32(args.reward_address) if args.reward_address else None diff --git a/pyproject.toml b/pyproject.toml index ec07c0ea..49395788 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "ledgercomm[hid]", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk==1.0.0" + { git = "https://github.com/multiversx/mx-sdk-py-cli.git", branch = "compute-bytes-for-signing" } ] [project.scripts] diff --git a/requirements.txt b/requirements.txt index c047d5a0..5dce815f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,5 @@ ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk==1.0.0 +# multiversx-sdk==1.0.0 +git+https://github.com/multiversx/mx-sdk-py@compute-bytes-for-signing#egg=multiversx_sdk From 8863c37e0928d7d997ca795e747e999efb4d3b3e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 3 Feb 2025 16:34:47 +0200 Subject: [PATCH 13/84] remove ledger package --- multiversx_sdk_cli/ledger/__init__.py | 0 multiversx_sdk_cli/ledger/config.py | 38 ---- .../ledger/ledger_app_handler.py | 164 ------------------ multiversx_sdk_cli/ledger/ledger_functions.py | 48 ----- requirements.txt | 2 +- 5 files changed, 1 insertion(+), 251 deletions(-) delete mode 100644 multiversx_sdk_cli/ledger/__init__.py delete mode 100644 multiversx_sdk_cli/ledger/config.py delete mode 100644 multiversx_sdk_cli/ledger/ledger_app_handler.py delete mode 100644 multiversx_sdk_cli/ledger/ledger_functions.py diff --git a/multiversx_sdk_cli/ledger/__init__.py b/multiversx_sdk_cli/ledger/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/multiversx_sdk_cli/ledger/config.py b/multiversx_sdk_cli/ledger/config.py deleted file mode 100644 index f9584006..00000000 --- a/multiversx_sdk_cli/ledger/config.py +++ /dev/null @@ -1,38 +0,0 @@ -class LedgerAppConfiguration: - data_activated: bool - account_index: int - address_index: int - version: str - - -def load_ledger_config_from_response(response: bytes) -> LedgerAppConfiguration: - config = LedgerAppConfiguration() - - config.data_activated = False - if response[0] == 0x01: - config.data_activated = True - - config.account_index = response[1] - config.address_index = response[2] - - version = str(response[3]) + "." + str(response[4]) + "." + str(response[5]) - config.version = version - - return config - - -def compare_versions(version1: str, version2: str) -> int: - version1_tuple = version_tuple(version1) - version2_tuple = version_tuple(version2) - if version1_tuple == version2_tuple: - return 0 - if version1_tuple < version2_tuple: - return -1 - return 1 - - -def version_tuple(v): - filled = [] - for point in v.split("."): - filled.append(point.zfill(8)) - return tuple(filled) diff --git a/multiversx_sdk_cli/ledger/ledger_app_handler.py b/multiversx_sdk_cli/ledger/ledger_app_handler.py deleted file mode 100644 index 18820813..00000000 --- a/multiversx_sdk_cli/ledger/ledger_app_handler.py +++ /dev/null @@ -1,164 +0,0 @@ -from typing import List - -from ledgercomm import Transport - -from multiversx_sdk_cli.errors import LedgerError -from multiversx_sdk_cli.ledger.config import ( - LedgerAppConfiguration, - load_ledger_config_from_response, -) - -SIGN_USING_HASH_VERSION = "1.0.11" -CONNECTION_ERROR_MSG = "check if device is plugged in, unlocked and on MultiversX app" - -# Also see: https://github.com/multiversx/mx-sdk-js-hw-provider/blob/main/src/ledgerApp.ts -CLA = 0xED -SIGN_RAW_TX_INS = 0x04 -SIGN_HASH_TX_INS = 0x07 -SIGN_MESSAGE_INS = 0x06 -PROVIDE_ESDT_INFO_INS = 0x08 -GET_ADDRESS_AUTH_TOKEN_INS = 0x09 - - -class Apdu: - cla: int - ins: int - p1: int - p2: int - data: bytes - - -class LedgerApp: - def __init__(self): - try: - self.transport = Transport(interface="hid", debug=False) # Nano S/X using HID interface - except Exception: - raise LedgerError(CONNECTION_ERROR_MSG) - - def close(self): - self.transport.close() - - def set_address(self, account_index: int = 0, address_index: int = 0): - data = account_index.to_bytes(4, byteorder="big") + address_index.to_bytes(4, byteorder="big") - self.transport.send(cla=0xED, ins=0x05, p1=0x00, p2=0x00, cdata=data) - sw, _ = self.transport.recv() - err = get_error(sw) - if err != "": - raise LedgerError(err) - - def get_address(self, account_index: int = 0, address_index: int = 0) -> str: - data = account_index.to_bytes(4, byteorder="big") + address_index.to_bytes(4, byteorder="big") - - self.transport.send(cla=0xED, ins=0x03, p1=0x00, p2=0x00, cdata=data) - sw, response = self.transport.recv() - assert isinstance(response, bytes) - - err = get_error(sw) - if err != "": - raise LedgerError(CONNECTION_ERROR_MSG + " (" + err + ")") - - response_body = response[1:] - address = response_body.decode("utf-8") - return address - - def get_app_configuration(self) -> LedgerAppConfiguration: - self.transport.send(cla=0xED, ins=0x02, p1=0x00, p2=0x00, cdata=b"") - sw, response = self.transport.recv() - err = get_error(sw) - if err != "": - raise LedgerError(CONNECTION_ERROR_MSG + " (" + err + ")") - return load_ledger_config_from_response(response) - - def get_version(self) -> str: - config = self.get_app_configuration() - return config.version - - def sign_transaction(self, tx_bytes: bytes, should_use_hash_signing: bool) -> str: - ins_signing_method = SIGN_RAW_TX_INS - if should_use_hash_signing: - ins_signing_method = SIGN_HASH_TX_INS - - return self._do_sign(tx_bytes, ins_signing_method) - - def sign_message(self, message_bytes: bytes) -> str: - return self._do_sign(message_bytes, SIGN_MESSAGE_INS) - - def _do_sign(self, data: bytes, ins_signing_method: int) -> str: - total_size = len(data) - max_chunk_size = 150 - - apdus: List[Apdu] = [] - - offset = 0 - while offset != total_size: - is_first = offset == 0 - - apdu = Apdu() - - if is_first: - apdu.p1 = 0x00 - else: - apdu.p1 = 0x80 - - has_more = offset + max_chunk_size < total_size - chunk_size = total_size - offset - if has_more: - chunk_size = max_chunk_size - - apdu.ins = ins_signing_method - apdu.p2 = 0x00 - apdu.cla = CLA - apdu.data = data[offset : offset + chunk_size] - - apdus.append(apdu) - - offset += chunk_size - - return self.get_signature_from_apdus(apdus) - - def get_signature_from_apdus(self, apdus: List[Apdu]) -> str: - sw: int - response: bytes - for apdu in apdus: - self.transport.send(cla=apdu.cla, ins=apdu.ins, p1=apdu.p1, p2=apdu.p2, cdata=apdu.data) - sw, response = self.transport.recv() - - assert isinstance(response, bytes) - if len(response) != 65 or response[0] != 64 or get_error(sw) != "": - err_message = "signature failed" - err = get_error(sw) - if err != "": - err_message += ": " + err - raise LedgerError(err_message) - - response_body = response[1:] - signature = response_body.hex() - return signature - - -def get_error(code: int): - switcher = { - 0x9000: "", - 0x6985: "user denied", - 0x6D00: "unknown instruction", - 0x6E00: "wrong cla", - 0x6E10: "signature failed", - 0x6E01: "invalid arguments", - 0x6E02: "invalid message", - 0x6E03: "invalid p1", - 0x6E04: "message too long", - 0x6E05: "receiver too long", - 0x6E06: "amount too long", - 0x6E07: "contract data disabled", - 0x6E08: "message incomplete", - 0x6E09: "wrong tx version", - 0x6E0A: "nonce too long", - 0x6E0B: "invalid amount", - 0x6E0C: "invalid fee", - 0x6E0D: "pretty failed", - 0x6E0E: "data too long", - 0x6E0F: "wrong tx options", - 0x6E11: "regular signing is deprecated", - } - - return switcher.get(code, "unknown error code: " + hex(code)) diff --git a/multiversx_sdk_cli/ledger/ledger_functions.py b/multiversx_sdk_cli/ledger/ledger_functions.py deleted file mode 100644 index d1ec64f8..00000000 --- a/multiversx_sdk_cli/ledger/ledger_functions.py +++ /dev/null @@ -1,48 +0,0 @@ -import logging - -from multiversx_sdk_cli.ledger.ledger_app_handler import LedgerApp - -TX_HASH_SIGN_VERSION = 2 -TX_HASH_SIGN_OPTIONS = 1 - -logger = logging.getLogger("ledger") - - -def do_sign_transaction_with_ledger( - tx_payload: bytes, account_index: int, address_index: int, sign_using_hash: bool -) -> str: - ledger_handler = LedgerApp() - ledger_handler.set_address(account_index=account_index, address_index=address_index) - - logger.info("Ledger: please confirm the transaction on the device") - signature = ledger_handler.sign_transaction(tx_payload, sign_using_hash) - ledger_handler.close() - - return signature - - -def do_sign_message_with_ledger(message_payload: bytes, account_index: int, address_index: int) -> str: - ledger_handler = LedgerApp() - ledger_handler.set_address(account_index=account_index, address_index=address_index) - - logger.info("Ledger: please confirm the message on the device") - signature = ledger_handler.sign_message(message_payload) - ledger_handler.close() - - return signature - - -def do_get_ledger_address(account_index: int, address_index: int) -> str: - ledger_handler = LedgerApp() - ledger_address = ledger_handler.get_address(account_index=account_index, address_index=address_index) - ledger_handler.close() - - return ledger_address - - -def do_get_ledger_version() -> str: - ledger_handler = LedgerApp() - ledger_version = ledger_handler.get_version() - ledger_handler.close() - - return ledger_version diff --git a/requirements.txt b/requirements.txt index 5dce815f..65b09768 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,5 +8,5 @@ ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 -# multiversx-sdk==1.0.0 +# multiversx-sdk[ledger]==1.0.0 git+https://github.com/multiversx/mx-sdk-py@compute-bytes-for-signing#egg=multiversx_sdk From e8d805d24ddff316cbf017a24d12aa5d9c72c8cf Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 3 Feb 2025 17:08:03 +0200 Subject: [PATCH 14/84] fix cli ledger --- multiversx_sdk_cli/cli_ledger.py | 5 +++-- multiversx_sdk_cli/tests/test_ledger.py | 17 ----------------- 2 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 multiversx_sdk_cli/tests/test_ledger.py diff --git a/multiversx_sdk_cli/cli_ledger.py b/multiversx_sdk_cli/cli_ledger.py index 25c2ad1e..db6fa2e0 100644 --- a/multiversx_sdk_cli/cli_ledger.py +++ b/multiversx_sdk_cli/cli_ledger.py @@ -1,8 +1,9 @@ import logging from typing import Any +from multiversx_sdk import LedgerApp + from multiversx_sdk_cli import cli_shared -from multiversx_sdk_cli.ledger.ledger_app_handler import LedgerApp logger = logging.getLogger("cli.ledger") @@ -35,7 +36,7 @@ def setup_parser(subparsers: Any) -> Any: def print_addresses(args: Any): ledger_app = LedgerApp() for i in range(args.num_addresses): - address = ledger_app.get_address(0, i) + address = ledger_app.get_address(i) print("account index = %d | address index = %d | address: %s" % (0, i, address)) ledger_app.close() diff --git a/multiversx_sdk_cli/tests/test_ledger.py b/multiversx_sdk_cli/tests/test_ledger.py deleted file mode 100644 index fff0b272..00000000 --- a/multiversx_sdk_cli/tests/test_ledger.py +++ /dev/null @@ -1,17 +0,0 @@ -from multiversx_sdk_cli.ledger.config import compare_versions -from multiversx_sdk_cli.ledger.ledger_app_handler import get_error - - -class TestLedger: - def test_compare_versions(self): - assert compare_versions("v1.0.0", "v1.0.1") == -1 - assert compare_versions("v1.0.1", "v1.0.1") == 0 - assert compare_versions("v1.0.1", "v1.0.0") == 1 - assert compare_versions("v1.0.0.1", "v1.0.0") == 1 - assert compare_versions("v1.0.1", "v1.0.1.0.0.4") == -1 - - def test_get_error(self): - assert get_error(0x6E0C) == "invalid fee" - assert get_error(0x6E11) == "regular signing is deprecated" - assert get_error(0x9000) == "" - assert get_error(0x9999999999) == "unknown error code: 0x9999999999" From 8bc1860b4fdfbbd0bdbd50f923e85eb73274b51d Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 3 Feb 2025 17:18:11 +0200 Subject: [PATCH 15/84] fix dependency for sdk-py --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 49395788..3a016ccd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "ledgercomm[hid]", "rich==13.3.4", "argcomplete==3.2.2", - { git = "https://github.com/multiversx/mx-sdk-py-cli.git", branch = "compute-bytes-for-signing" } + "multiversx-sdk[ledger]==1.0.0" ] [project.scripts] From 8b9fd8aa7b0b075f94a20ba903c1b64195598d0d Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 4 Feb 2025 13:12:06 +0200 Subject: [PATCH 16/84] reference latest sdk-py --- pyproject.toml | 2 +- requirements.txt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3a016ccd..73ce9ec2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "ledgercomm[hid]", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk[ledger]==1.0.0" + "multiversx-sdk[ledger]==1.1.0" ] [project.scripts] diff --git a/requirements.txt b/requirements.txt index 65b09768..bd3ddbe8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,5 +8,4 @@ ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 -# multiversx-sdk[ledger]==1.0.0 -git+https://github.com/multiversx/mx-sdk-py@compute-bytes-for-signing#egg=multiversx_sdk +multiversx-sdk[ledger]==1.1.0 From f0ecfb3f0608fb9b53fb557b38d82f3edd45b6b7 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 4 Feb 2025 18:53:06 +0200 Subject: [PATCH 17/84] refactor tranasctions creation --- multiversx_sdk_cli/cli_contracts.py | 8 +- multiversx_sdk_cli/cli_shared.py | 32 ++++--- multiversx_sdk_cli/cli_transactions.py | 90 +++++++++++++++---- multiversx_sdk_cli/cli_validators.py | 58 ++++++++++-- multiversx_sdk_cli/config.py | 5 -- multiversx_sdk_cli/constants.py | 4 + multiversx_sdk_cli/cosign_transaction.py | 4 +- multiversx_sdk_cli/shared.py | 42 +++++++++ .../tests/test_cli_transactions.py | 34 +++++++ multiversx_sdk_cli/transactions.py | 52 ++++++++++- multiversx_sdk_cli/validators/core.py | 8 +- 11 files changed, 279 insertions(+), 58 deletions(-) create mode 100644 multiversx_sdk_cli/shared.py diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index af04f617..57f8f9ea 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -345,7 +345,7 @@ def deploy(args: Any): sender = cli_shared.prepare_account(args) - if not args.nonce: + if args.nonce is None: nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) else: nonce = int(args.nonce) @@ -395,7 +395,7 @@ def _sign_guarded_tx_if_guardian(guardian: Union[IAccount, None], args: Any, tx: if guardian: tx.guardian_signature = guardian.sign_transaction(tx) elif tx.guardian and args.guardian_service_url and args.guardian_2fa_code: - tx = cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) + cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) return tx @@ -413,7 +413,7 @@ def call(args: Any): sender = cli_shared.prepare_account(args) - if not args.nonce: + if args.nonce is None: nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) else: nonce = int(args.nonce) @@ -460,7 +460,7 @@ def upgrade(args: Any): sender = cli_shared.prepare_account(args) - if not args.nonce: + if args.nonce is None: nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) else: nonce = int(args.nonce) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 37072185..f75078a7 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -22,10 +22,11 @@ load_relayer_password, ) from multiversx_sdk_cli.constants import ( + DEFAULT_GAS_PRICE, DEFAULT_TX_VERSION, TRANSACTION_OPTIONS_TX_GUARDED, ) -from multiversx_sdk_cli.errors import ArgumentsNotProvidedError +from multiversx_sdk_cli.errors import ArgumentsNotProvidedError, IncorrectWalletError from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.simulation import Simulator from multiversx_sdk_cli.transactions import send_and_wait_for_result @@ -85,6 +86,7 @@ def add_tx_args( "--nonce", type=int, required=not ("--recall-nonce" in args), + default=None, help="# the nonce for the transaction", ) sub.add_argument( @@ -100,10 +102,12 @@ def add_tx_args( sub.add_argument( "--gas-price", - default=config.DEFAULT_GAS_PRICE, + default=DEFAULT_GAS_PRICE, + type=int, help="⛽ the gas price (default: %(default)d)", ) - sub.add_argument("--gas-limit", required=not ("--estimate-gas" in args), help="⛽ the gas limit") + sub.add_argument("--gas-limit", required=not ("--estimate-gas" in args), type=int, help="⛽ the gas limit") + if with_estimate_gas: sub.add_argument( "--estimate-gas", @@ -112,7 +116,7 @@ def add_tx_args( help="⛽ whether to estimate the gas limit (default: %(default)d)", ) - sub.add_argument("--value", default="0", help="the value to transfer (default: %(default)s)") + sub.add_argument("--value", default="0", type=int, help="the value to transfer (default: %(default)s)") if with_data: sub.add_argument( @@ -398,23 +402,23 @@ def load_guardian_account(args: Any) -> Union[IAccount, None]: def get_guardian_address(guardian: Union[IAccount, None], args: Any) -> Union[Address, None]: - if guardian: - return guardian.address + address_pem = guardian.address if guardian else None + address_arg = Address.new_from_bech32(args.guardian) if hasattr(args, "guardian") and args.guardian else None - if hasattr(args, "guardian") and args.guardian: - return Address.new_from_bech32(args.guardian) + if address_pem and address_arg and address_pem != address_arg: + raise IncorrectWalletError("Guardian wallet does not match the guardian's address set in the transaction.") - return None + return address_pem or address_arg def get_relayer_address(relayer: Union[IAccount, None], args: Any) -> Union[Address, None]: - if relayer: - return relayer.address + address_pem = relayer.address if relayer else None + address_arg = Address.new_from_bech32(args.relayer) if hasattr(args, "relayer") and args.relayer else None - if hasattr(args, "relayer") and args.relayer: - return Address.new_from_bech32(args.relayer) + if address_pem and address_arg and address_pem != address_arg: + raise IncorrectWalletError("Relayer wallet does not match the relayer's address set in the transaction.") - return None + return address_pem or address_arg def load_relayer_account(args: Any) -> Union[IAccount, None]: diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 6feb32e5..963a5a72 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -1,23 +1,29 @@ import logging from pathlib import Path -from typing import Any, List - -from multiversx_sdk import ProxyNetworkProvider +from typing import Any + +from multiversx_sdk import ( + Address, + ProxyNetworkProvider, + Token, + TokenComputer, + TokenTransfer, +) -from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli import cli_shared, shared, utils from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.errors import IncorrectWalletError, NoWalletProvided from multiversx_sdk_cli.transactions import ( - do_prepare_transaction, + TransactionsController, load_transaction_from_file, ) logger = logging.getLogger("cli.transactions") -def setup_parser(args: List[str], subparsers: Any) -> Any: +def setup_parser(args: list[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser(subparsers, "tx", "Create and broadcast Transactions") subparsers = parser.add_subparsers() @@ -109,31 +115,81 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: return subparsers -def _add_common_arguments(args: List[str], sub: Any): +def _add_common_arguments(args: list[str], sub: Any): cli_shared.add_wallet_args(args, sub) cli_shared.add_tx_args(args, sub) sub.add_argument("--data-file", type=str, default=None, help="a file containing transaction data") def create_transaction(args: Any): - args = utils.as_object(args) - cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_chain_id_in_args(args) - cli_shared.prepare_nonce_in_args(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) if args.data_file: args.data = Path(args.data_file).read_text() - tx = do_prepare_transaction(args) + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + transfers = getattr(args, "token_transfers", None) + transfers = prepare_token_transfers(transfers) if transfers else None + + tx_controller = TransactionsController(args.chain) + tx = tx_controller.create_transaction_for_transfer( + sender=sender.address, + receiver=Address.new_from_bech32(args.receiver), + native_amount=native_amount, + gas_limt=args.gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + token_transfers=transfers, + data=args.data, + guardian=guardian_address, + relayer=relayer_address, + ) + + shared.set_options_for_guarded_transaction_if_needed(tx) + shared.set_options_for_hash_signing_if_needed(tx, sender, guardian, relayer) + + tx.signature = sender.sign_transaction(tx) + + shared.sign_guarded_transaction_if_guardian(tx, guardian, args.guardian_service_url, args.guardian_2fa_code) + shared.sign_relayed_transaction_if_relayer(tx, relayer) cli_shared.send_or_simulate(tx, args) -def send_transaction(args: Any): - args = utils.as_object(args) +def prepare_token_transfers(transfers: list[Any]) -> list[TokenTransfer]: + token_computer = TokenComputer() + token_transfers: list[TokenTransfer] = [] + + for i in range(0, len(transfers) - 1, 2): + identifier = transfers[i] + amount = int(transfers[i + 1]) + nonce = token_computer.extract_nonce_from_extended_identifier(identifier) + + token = Token(identifier, nonce) + transfer = TokenTransfer(token, amount) + token_transfers.append(transfer) + + return token_transfers + +def send_transaction(args: Any): tx = load_transaction_from_file(args.infile) output = CLIOutputBuilder() @@ -149,7 +205,7 @@ def send_transaction(args: Any): def get_transaction(args: Any): - args = utils.as_object(args) + # args = utils.as_object(args) omit_fields = cli_shared.parse_omit_fields_arg(args) config = get_config_for_network_providers() @@ -161,8 +217,6 @@ def get_transaction(args: Any): def sign_transaction(args: Any): - args = utils.as_object(args) - cli_shared.check_guardian_args(args) cli_shared.check_broadcast_args(args) @@ -181,14 +235,12 @@ def sign_transaction(args: Any): if guardian_account: tx.guardian_signature = guardian_account.sign_transaction(tx) elif args.guardian: - tx = cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) + cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) cli_shared.send_or_simulate(tx, args) def relay_transaction(args: Any): - args = utils.as_object(args) - if not _is_relayer_wallet_provided(args): raise NoWalletProvided() diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 51e7f09c..8059a792 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -1,7 +1,12 @@ from typing import Any, List -from multiversx_sdk_cli import cli_shared, utils, validators -from multiversx_sdk_cli.transactions import do_prepare_transaction +from multiversx_sdk import Address + +from multiversx_sdk_cli import cli_shared, shared, utils, validators +from multiversx_sdk_cli.transactions import ( + TransactionsController, + do_prepare_transaction, +) def setup_parser(args: List[str], subparsers: Any) -> Any: @@ -136,11 +141,54 @@ def _add_nodes_arg(sub: Any): def do_stake(args: Any): + # cli_shared.check_broadcast_args(args) + # cli_shared.prepare_nonce_in_args(args) + # cli_shared.prepare_chain_id_in_args(args) + # validators.prepare_args_for_stake(args) + # tx = do_prepare_transaction(args) + + # cli_shared.send_or_simulate(tx, args) + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_stake(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + + tx_controller = TransactionsController(args.chain) + tx = tx_controller.create_transaction_for_transfer( + sender=sender.address, + receiver=Address.new_from_bech32(args.receiver), + native_amount=native_amount, + gas_limt=args.gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + data=args.data, + guardian=guardian_address, + relayer=relayer_address, + ) + + shared.set_options_for_guarded_transaction_if_needed(tx) + shared.set_options_for_hash_signing_if_needed(tx, sender, guardian, relayer) + + tx.signature = sender.sign_transaction(tx) + + shared.sign_guarded_transaction_if_guardian(tx, guardian, args.guardian_service_url, args.guardian_2fa_code) + shared.sign_relayed_transaction_if_relayer(tx, relayer) cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index a9abbe7d..d5e61b52 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -10,11 +10,6 @@ LOCAL_CONFIG_PATH = Path("mxpy.json").resolve() GLOBAL_CONFIG_PATH = SDK_PATH / "mxpy.json" -DEFAULT_GAS_PRICE = 1000000000 -GAS_PER_DATA_BYTE = 1500 -MIN_GAS_LIMIT = 50000 -MAX_GAS_LIMIT = 600000000 - class MetaChainSystemSCsCost: STAKE = 5000000 diff --git a/multiversx_sdk_cli/constants.py b/multiversx_sdk_cli/constants.py index 0f0e0dbc..ce1b5e5d 100644 --- a/multiversx_sdk_cli/constants.py +++ b/multiversx_sdk_cli/constants.py @@ -12,3 +12,7 @@ ADDRESS_ZERO_HEX = "0000000000000000000000000000000000000000000000000000000000000000" NUMBER_OF_SHARDS = 3 + +DEFAULT_GAS_PRICE = 1000000000 +GAS_PER_DATA_BYTE = 1500 +MIN_GAS_LIMIT = 50000 diff --git a/multiversx_sdk_cli/cosign_transaction.py b/multiversx_sdk_cli/cosign_transaction.py index f269ac4c..4a383ddf 100644 --- a/multiversx_sdk_cli/cosign_transaction.py +++ b/multiversx_sdk_cli/cosign_transaction.py @@ -6,7 +6,7 @@ from multiversx_sdk_cli.errors import GuardianServiceError -def cosign_transaction(transaction: Transaction, service_url: str, guardian_code: str) -> Transaction: +def cosign_transaction(transaction: Transaction, service_url: str, guardian_code: str): payload = { "code": f"{guardian_code}", "transactions": [transaction.to_dictionary()], @@ -21,8 +21,6 @@ def cosign_transaction(transaction: Transaction, service_url: str, guardian_code tx_as_dict = response.json()["data"]["transactions"][0] transaction.guardian_signature = bytes.fromhex(tx_as_dict["guardianSignature"]) - return transaction - def check_for_guardian_error(response: dict[str, Any]): error = response["error"] diff --git a/multiversx_sdk_cli/shared.py b/multiversx_sdk_cli/shared.py new file mode 100644 index 00000000..b128c72e --- /dev/null +++ b/multiversx_sdk_cli/shared.py @@ -0,0 +1,42 @@ +from typing import Union + +from multiversx_sdk import LedgerAccount, Transaction, TransactionComputer + +from multiversx_sdk_cli.cosign_transaction import cosign_transaction +from multiversx_sdk_cli.interfaces import IAccount + + +def set_options_for_guarded_transaction_if_needed(transaction: Transaction): + if transaction.guardian: + transaction_computer = TransactionComputer() + transaction_computer.apply_guardian(transaction, transaction.guardian) + + +def set_options_for_hash_signing_if_needed( + transaction: Transaction, + sender: IAccount, + guardian: Union[IAccount, None], + relayer: Union[IAccount, None], +): + if isinstance(sender, LedgerAccount) or isinstance(guardian, LedgerAccount) or isinstance(relayer, LedgerAccount): + transaction_computer = TransactionComputer() + transaction_computer.apply_options_for_hash_signing(transaction) + + +def sign_guarded_transaction_if_guardian( + transaction: Transaction, + guardian: Union[IAccount, None], + guardian_service_url: str, + guardian_2fa_code: str, +) -> Transaction: + if guardian: + transaction.guardian_signature = guardian.sign_transaction(transaction) + elif transaction.guardian and guardian_service_url and guardian_2fa_code: + cosign_transaction(transaction, guardian_service_url, guardian_2fa_code) + + return transaction + + +def sign_relayed_transaction_if_relayer(transaction: Transaction, relayer: Union[IAccount, None]): + if relayer and transaction.relayer: + transaction.relayer_signature = relayer.sign_transaction(transaction) diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index b9c3cea2..3dd83bcb 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -294,5 +294,39 @@ def test_check_relayer_wallet_is_provided(): assert return_code +def test_create_plain_transaction(capsys: Any): + return_code = main( + [ + "tx", + "new", + "--pem", + str(testdata_path / "alice.pem"), + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", + "89", + "--gas-limit", + "50000", + "--chain", + "integration tests chain ID", + ] + ) + assert return_code == 0 + + tx = _read_stdout(capsys) + tx_json = json.loads(tx)["emittedTransaction"] + + assert tx_json["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx_json["receiver"] == "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" + assert tx_json["chainID"] == "integration tests chain ID" + assert tx_json["gasLimit"] == 50000 + assert tx_json["version"] == 2 + assert tx_json["options"] == 0 + assert ( + tx_json["signature"] + == "210d3e75924858cde8c7f3020b38db32890a79101cadcb32dc04980e4f3a378a14e9517ff805881f6444efc61fb38b7dfcf3fee07d4b87fa254ee96b67681e02" + ) + + def _read_stdout(capsys: Any) -> str: return capsys.readouterr().out.strip() diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 402fa04a..23dbbca5 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -2,7 +2,7 @@ import logging import time from pathlib import Path -from typing import Any, List, Protocol, TextIO, Union +from typing import Any, List, Optional, Protocol, TextIO, Union from multiversx_sdk import ( Account, @@ -37,6 +37,54 @@ def get_transaction(self, transaction_hash: Union[bytes, str]) -> TransactionOnN # fmt: on +class TransactionsController: + def __init__(self, chain_id: str) -> None: + config = TransactionsFactoryConfig(chain_id) + self.factory = TransferTransactionsFactory(config) + + def create_transaction_for_transfer( + self, + sender: Address, + receiver: Address, + native_amount: int, + gas_limt: int, + gas_price: int, + nonce: int, + version: int, + options: int, + token_transfers: Optional[list[TokenTransfer]] = None, + data: Optional[str] = None, + guardian: Optional[Address] = None, + relayer: Optional[Address] = None, + ) -> Transaction: + # if no value, token transfers or data provided, create plain transaction + if not native_amount and not token_transfers and not data: + transaction = Transaction( + sender=sender, + receiver=receiver, + gas_limit=gas_limt, + chain_id=self.factory.config.chain_id, + ) + else: + transaction = self.factory.create_transaction_for_transfer( + sender=sender, + receiver=receiver, + native_amount=native_amount, + token_transfers=token_transfers, + data=data.encode() if data else None, + ) + + transaction.gas_limit = gas_limt + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian + transaction.relayer = relayer + + return transaction + + def do_prepare_transaction(args: Any) -> Transaction: account = load_sender_account_from_args(args) @@ -175,7 +223,7 @@ def sign_tx_by_guardian(args: Any, tx: Transaction, guardian_account: Union[IAcc if guardian_account: tx.guardian_signature = guardian_account.sign_transaction(tx) elif args.guardian: - tx = cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) # type: ignore + cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) return tx diff --git a/multiversx_sdk_cli/validators/core.py b/multiversx_sdk_cli/validators/core.py index 5a6bbd2a..de37b597 100644 --- a/multiversx_sdk_cli/validators/core.py +++ b/multiversx_sdk_cli/validators/core.py @@ -5,12 +5,8 @@ from multiversx_sdk import Address, ValidatorPEM, ValidatorSigner from multiversx_sdk_cli import cli_shared, utils -from multiversx_sdk_cli.config import ( - GAS_PER_DATA_BYTE, - MIN_GAS_LIMIT, - MetaChainSystemSCsCost, - get_address_hrp, -) +from multiversx_sdk_cli.config import MetaChainSystemSCsCost, get_address_hrp +from multiversx_sdk_cli.constants import GAS_PER_DATA_BYTE, MIN_GAS_LIMIT from multiversx_sdk_cli.contracts import prepare_execute_transaction_data from multiversx_sdk_cli.validators.validators_file import ValidatorsFile From 7670cb10fa33866d37bd29ec6a38bdb511b6a7b8 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 5 Feb 2025 11:19:52 +0200 Subject: [PATCH 18/84] use interface instead of Account class --- multiversx_sdk_cli/cli_contracts.py | 4 +--- multiversx_sdk_cli/contract_verification.py | 25 +++++++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index af04f617..fd124d19 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -2,10 +2,9 @@ import logging import os from pathlib import Path -from typing import Any, Union, cast +from typing import Any, Union from multiversx_sdk import ( - Account, Address, AddressComputer, ProxyNetworkProvider, @@ -556,7 +555,6 @@ def verify(args: Any) -> None: packaged_src = Path(args.packaged_src).expanduser().resolve() owner = cli_shared.prepare_account(args) - owner = cast(Account, owner) docker_image = args.docker_image contract_variant = args.contract_variant diff --git a/multiversx_sdk_cli/contract_verification.py b/multiversx_sdk_cli/contract_verification.py index 561e3828..3a9221c3 100644 --- a/multiversx_sdk_cli/contract_verification.py +++ b/multiversx_sdk_cli/contract_verification.py @@ -3,10 +3,10 @@ import logging import time from pathlib import Path -from typing import Any, Dict, Optional, Tuple +from typing import Any, Optional, Protocol, Tuple import requests -from multiversx_sdk import Account, Address, Message +from multiversx_sdk import Address, Message from multiversx_sdk_cli.errors import KnownError from multiversx_sdk_cli.utils import dump_out_json, read_json_file @@ -17,11 +17,18 @@ logger = logging.getLogger("cli.contracts.verifier") +# fmt: off +class IAccount(Protocol): + def sign_message(self, message: Message) -> bytes: + ... +# fmt: on + + class ContractVerificationRequest: def __init__( self, contract: Address, - source_code: Dict[str, Any], + source_code: dict[str, Any], signature: bytes, docker_image: str, contract_variant: Optional[str], @@ -32,7 +39,7 @@ def __init__( self.docker_image = docker_image self.contract_variant = contract_variant - def to_dictionary(self) -> Dict[str, Any]: + def to_dictionary(self) -> dict[str, Any]: return { "signature": self.signature.hex(), "payload": { @@ -48,7 +55,7 @@ class ContractVerificationPayload: def __init__( self, contract: Address, - source_code: Dict[str, Any], + source_code: dict[str, Any], docker_image: str, contract_variant: Optional[str], ) -> None: @@ -70,7 +77,7 @@ def serialize(self) -> str: def trigger_contract_verification( packaged_source: Path, - owner: Account, + owner: IAccount, contract: Address, verifier_url: str, docker_image: str, @@ -109,7 +116,7 @@ def trigger_contract_verification( query_status_with_task_id(verifier_url, task_id) -def _create_request_signature(account: Account, contract_address: Address, request_payload: bytes) -> bytes: +def _create_request_signature(account: IAccount, contract_address: Address, request_payload: bytes) -> bytes: hashed_payload: str = hashlib.sha256(request_payload).hexdigest() raw_data_to_sign = f"{contract_address.to_bech32()}{hashed_payload}" @@ -136,7 +143,7 @@ def query_status_with_task_id(url: str, task_id: str, interval: int = 10): time.sleep(interval) -def _do_post(url: str, payload: Any) -> Tuple[int, str, Dict[str, Any]]: +def _do_post(url: str, payload: Any) -> Tuple[int, str, dict[str, Any]]: logger.debug(f"_do_post() to {url}") response = requests.post(url, json=payload) @@ -149,7 +156,7 @@ def _do_post(url: str, payload: Any) -> Tuple[int, str, Dict[str, Any]]: raise KnownError(f"Cannot parse response from {url}", error) -def _do_get(url: str) -> Tuple[int, str, Dict[str, Any]]: +def _do_get(url: str) -> Tuple[int, str, dict[str, Any]]: logger.debug(f"_do_get() from {url}") response = requests.get(url) From 10f570f632b6d030564eb1b4e43eb6e132251d11 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 5 Feb 2025 14:11:07 +0200 Subject: [PATCH 19/84] refactor transaction creation --- .../base_transactions_controller.py | 72 +++++++++++++++++++ multiversx_sdk_cli/cli_transactions.py | 22 +++--- multiversx_sdk_cli/interfaces.py | 1 - multiversx_sdk_cli/shared.py | 42 ----------- multiversx_sdk_cli/transactions.py | 32 ++++++--- 5 files changed, 104 insertions(+), 65 deletions(-) create mode 100644 multiversx_sdk_cli/base_transactions_controller.py delete mode 100644 multiversx_sdk_cli/shared.py diff --git a/multiversx_sdk_cli/base_transactions_controller.py b/multiversx_sdk_cli/base_transactions_controller.py new file mode 100644 index 00000000..e5e177f0 --- /dev/null +++ b/multiversx_sdk_cli/base_transactions_controller.py @@ -0,0 +1,72 @@ +from typing import Optional, Union + +from multiversx_sdk import LedgerAccount, Transaction, TransactionComputer + +from multiversx_sdk_cli.cosign_transaction import cosign_transaction +from multiversx_sdk_cli.interfaces import IAccount + + +class BaseTransactionsController: + def __init__(self) -> None: + pass + + def sign_transaction( + self, + transaction: Transaction, + sender: IAccount, + guardian: Optional[IAccount] = None, + relayer: Optional[IAccount] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ): + """Signs the transaction with the sender's account and if necessarry, signs with guardian's account and relayer's account. Also sets proper transaction options if needed.""" + self._set_options_for_guarded_transaction_if_needed(transaction) + self._set_options_for_hash_signing_if_needed(transaction, sender, guardian, relayer) + + transaction.signature = sender.sign_transaction(transaction) + + self._sign_guarded_transaction_if_guardian( + transaction, + guardian, + guardian_service_url, + guardian_2fa_code, + ) + self._sign_relayed_transaction_if_relayer(transaction, relayer) + + def _set_options_for_guarded_transaction_if_needed(self, transaction: Transaction): + if transaction.guardian: + transaction_computer = TransactionComputer() + transaction_computer.apply_guardian(transaction, transaction.guardian) + + def _set_options_for_hash_signing_if_needed( + self, + transaction: Transaction, + sender: IAccount, + guardian: Union[IAccount, None], + relayer: Union[IAccount, None], + ): + if ( + isinstance(sender, LedgerAccount) + or isinstance(guardian, LedgerAccount) + or isinstance(relayer, LedgerAccount) + ): + transaction_computer = TransactionComputer() + transaction_computer.apply_options_for_hash_signing(transaction) + + def _sign_guarded_transaction_if_guardian( + self, + transaction: Transaction, + guardian: Union[IAccount, None], + guardian_service_url: str, + guardian_2fa_code: str, + ) -> Transaction: + if guardian: + transaction.guardian_signature = guardian.sign_transaction(transaction) + elif transaction.guardian and guardian_service_url and guardian_2fa_code: + cosign_transaction(transaction, guardian_service_url, guardian_2fa_code) + + return transaction + + def _sign_relayed_transaction_if_relayer(self, transaction: Transaction, relayer: Union[IAccount, None]): + if relayer and transaction.relayer: + transaction.relayer_signature = relayer.sign_transaction(transaction) diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 963a5a72..8c0608ad 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -10,7 +10,7 @@ TokenTransfer, ) -from multiversx_sdk_cli import cli_shared, shared, utils +from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.cosign_transaction import cosign_transaction @@ -147,8 +147,8 @@ def create_transaction(args: Any): transfers = prepare_token_transfers(transfers) if transfers else None tx_controller = TransactionsController(args.chain) - tx = tx_controller.create_transaction_for_transfer( - sender=sender.address, + tx = tx_controller.create_transaction( + sender=sender, receiver=Address.new_from_bech32(args.receiver), native_amount=native_amount, gas_limt=args.gas_limit, @@ -158,18 +158,14 @@ def create_transaction(args: Any): options=args.options, token_transfers=transfers, data=args.data, - guardian=guardian_address, - relayer=relayer_address, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, ) - shared.set_options_for_guarded_transaction_if_needed(tx) - shared.set_options_for_hash_signing_if_needed(tx, sender, guardian, relayer) - - tx.signature = sender.sign_transaction(tx) - - shared.sign_guarded_transaction_if_guardian(tx, guardian, args.guardian_service_url, args.guardian_2fa_code) - shared.sign_relayed_transaction_if_relayer(tx, relayer) - cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/interfaces.py b/multiversx_sdk_cli/interfaces.py index 67066828..b6d70e18 100644 --- a/multiversx_sdk_cli/interfaces.py +++ b/multiversx_sdk_cli/interfaces.py @@ -25,7 +25,6 @@ class ITransaction(Protocol): class IAccount(Protocol): - use_hash_signing: bool address: Address def sign_transaction(self, transaction: Transaction) -> bytes: diff --git a/multiversx_sdk_cli/shared.py b/multiversx_sdk_cli/shared.py deleted file mode 100644 index b128c72e..00000000 --- a/multiversx_sdk_cli/shared.py +++ /dev/null @@ -1,42 +0,0 @@ -from typing import Union - -from multiversx_sdk import LedgerAccount, Transaction, TransactionComputer - -from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.interfaces import IAccount - - -def set_options_for_guarded_transaction_if_needed(transaction: Transaction): - if transaction.guardian: - transaction_computer = TransactionComputer() - transaction_computer.apply_guardian(transaction, transaction.guardian) - - -def set_options_for_hash_signing_if_needed( - transaction: Transaction, - sender: IAccount, - guardian: Union[IAccount, None], - relayer: Union[IAccount, None], -): - if isinstance(sender, LedgerAccount) or isinstance(guardian, LedgerAccount) or isinstance(relayer, LedgerAccount): - transaction_computer = TransactionComputer() - transaction_computer.apply_options_for_hash_signing(transaction) - - -def sign_guarded_transaction_if_guardian( - transaction: Transaction, - guardian: Union[IAccount, None], - guardian_service_url: str, - guardian_2fa_code: str, -) -> Transaction: - if guardian: - transaction.guardian_signature = guardian.sign_transaction(transaction) - elif transaction.guardian and guardian_service_url and guardian_2fa_code: - cosign_transaction(transaction, guardian_service_url, guardian_2fa_code) - - return transaction - - -def sign_relayed_transaction_if_relayer(transaction: Transaction, relayer: Union[IAccount, None]): - if relayer and transaction.relayer: - transaction.relayer_signature = relayer.sign_transaction(transaction) diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 23dbbca5..1cc478f2 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -19,6 +19,7 @@ ) from multiversx_sdk_cli import config, errors +from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.cli_password import load_guardian_password, load_password from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.errors import IncorrectWalletError, NoWalletProvided @@ -37,14 +38,14 @@ def get_transaction(self, transaction_hash: Union[bytes, str]) -> TransactionOnN # fmt: on -class TransactionsController: +class TransactionsController(BaseTransactionsController): def __init__(self, chain_id: str) -> None: config = TransactionsFactoryConfig(chain_id) self.factory = TransferTransactionsFactory(config) - def create_transaction_for_transfer( + def create_transaction( self, - sender: Address, + sender: IAccount, receiver: Address, native_amount: int, gas_limt: int, @@ -54,20 +55,24 @@ def create_transaction_for_transfer( options: int, token_transfers: Optional[list[TokenTransfer]] = None, data: Optional[str] = None, - guardian: Optional[Address] = None, - relayer: Optional[Address] = None, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", ) -> Transaction: # if no value, token transfers or data provided, create plain transaction if not native_amount and not token_transfers and not data: transaction = Transaction( - sender=sender, + sender=sender.address, receiver=receiver, gas_limit=gas_limt, chain_id=self.factory.config.chain_id, ) else: transaction = self.factory.create_transaction_for_transfer( - sender=sender, + sender=sender.address, receiver=receiver, native_amount=native_amount, token_transfers=token_transfers, @@ -79,8 +84,17 @@ def create_transaction_for_transfer( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian - transaction.relayer = relayer + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return transaction From d34b2b8f65670ebf63093b732e4718e14b7d5aea Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 5 Feb 2025 14:11:57 +0200 Subject: [PATCH 20/84] refactoring smart contract transactions --- multiversx_sdk_cli/cli_contracts.py | 48 ++++++++------------ multiversx_sdk_cli/contracts.py | 69 ++++++++++++++++++++++------- 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 57f8f9ea..0ef5282b 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -2,7 +2,7 @@ import logging import os from pathlib import Path -from typing import Any, Union, cast +from typing import Any, cast from multiversx_sdk import ( Account, @@ -20,10 +20,8 @@ from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS from multiversx_sdk_cli.contract_verification import trigger_contract_verification from multiversx_sdk_cli.contracts import SmartContract -from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.docker import is_docker_installed, run_docker from multiversx_sdk_cli.errors import DockerMissingError -from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.ux import show_warning logger = logging.getLogger("cli.contracts") @@ -376,11 +374,13 @@ def deploy(args: Any): nonce=nonce, version=int(args.version), options=int(args.options), - guardian=guardian_address, - relayer=relayer_address, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, ) - tx = _sign_guarded_tx_if_guardian(guardian, args, tx) - _sign_relayed_tx_if_relayer(relayer, tx) address_computer = AddressComputer(NUMBER_OF_SHARDS) contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=tx.nonce) @@ -391,20 +391,6 @@ def deploy(args: Any): _send_or_simulate(tx, contract_address, args) -def _sign_guarded_tx_if_guardian(guardian: Union[IAccount, None], args: Any, tx: Transaction) -> Transaction: - if guardian: - tx.guardian_signature = guardian.sign_transaction(tx) - elif tx.guardian and args.guardian_service_url and args.guardian_2fa_code: - cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) - - return tx - - -def _sign_relayed_tx_if_relayer(relayer: Union[IAccount, None], tx: Transaction): - if relayer and tx.relayer: - tx.relayer_signature = relayer.sign_transaction(tx) - - def call(args: Any): logger.debug("call") cli_shared.check_guardian_and_options_args(args) @@ -443,11 +429,13 @@ def call(args: Any): nonce=nonce, version=int(args.version), options=int(args.options), - guardian=guardian_address, - relayer=relayer_address, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, ) - tx = _sign_guarded_tx_if_guardian(guardian, args, tx) - _sign_relayed_tx_if_relayer(relayer, tx) _send_or_simulate(tx, contract_address, args) @@ -493,11 +481,13 @@ def upgrade(args: Any): nonce=nonce, version=int(args.version), options=int(args.options), - guardian=guardian_address, - relayer=relayer_address, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, ) - tx = _sign_guarded_tx_if_guardian(guardian, args, tx) - _sign_relayed_tx_if_relayer(relayer, tx) _send_or_simulate(tx, contract_address, args) diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index add4b855..947788cd 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -26,6 +26,7 @@ ) from multiversx_sdk_cli import errors +from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.interfaces import IAccount @@ -49,7 +50,7 @@ def await_transaction_completed( # fmt: on -class SmartContract: +class SmartContract(BaseTransactionsController): def __init__(self, config: TransactionsFactoryConfig, abi: Optional[Abi] = None): self._abi = abi self._config = config @@ -70,8 +71,12 @@ def prepare_deploy_transaction( nonce: int, version: int, options: int, - guardian: Union[Address, None], - relayer: Union[Address, None], + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: @@ -91,9 +96,17 @@ def prepare_deploy_transaction( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian - tx.relayer = relayer - tx.signature = owner.sign_transaction(tx) + tx.guardian = guardian_address + tx.relayer = relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx @@ -110,8 +123,12 @@ def prepare_execute_transaction( nonce: int, version: int, options: int, - guardian: Union[Address, None], - relayer: Union[Address, None], + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", ) -> Transaction: token_transfers = self._prepare_token_transfers(transfers) if transfers else [] @@ -131,9 +148,17 @@ def prepare_execute_transaction( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian - tx.relayer = relayer - tx.signature = caller.sign_transaction(tx) + tx.guardian = guardian_address + tx.relayer = relayer_address + + self.sign_transaction( + transaction=tx, + sender=caller, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx @@ -153,8 +178,12 @@ def prepare_upgrade_transaction( nonce: int, version: int, options: int, - guardian: Union[Address, None], - relayer: Union[Address, None], + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: @@ -175,9 +204,17 @@ def prepare_upgrade_transaction( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian - tx.relayer = relayer - tx.signature = owner.sign_transaction(tx) + tx.guardian = guardian_address + tx.relayer = relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx From 9b0759b4312e14e4fa1d0358ad8f2b5bc3962a81 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 5 Feb 2025 14:34:30 +0200 Subject: [PATCH 21/84] revert validators file --- multiversx_sdk_cli/cli_validators.py | 58 +++------------------------- 1 file changed, 5 insertions(+), 53 deletions(-) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 8059a792..51e7f09c 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -1,12 +1,7 @@ from typing import Any, List -from multiversx_sdk import Address - -from multiversx_sdk_cli import cli_shared, shared, utils, validators -from multiversx_sdk_cli.transactions import ( - TransactionsController, - do_prepare_transaction, -) +from multiversx_sdk_cli import cli_shared, utils, validators +from multiversx_sdk_cli.transactions import do_prepare_transaction def setup_parser(args: List[str], subparsers: Any) -> Any: @@ -141,54 +136,11 @@ def _add_nodes_arg(sub: Any): def do_stake(args: Any): - # cli_shared.check_broadcast_args(args) - # cli_shared.prepare_nonce_in_args(args) - # cli_shared.prepare_chain_id_in_args(args) - # validators.prepare_args_for_stake(args) - # tx = do_prepare_transaction(args) - - # cli_shared.send_or_simulate(tx, args) - cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) + cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) - - native_amount = int(args.value) - - tx_controller = TransactionsController(args.chain) - tx = tx_controller.create_transaction_for_transfer( - sender=sender.address, - receiver=Address.new_from_bech32(args.receiver), - native_amount=native_amount, - gas_limt=args.gas_limit, - gas_price=args.gas_price, - nonce=nonce, - version=args.version, - options=args.options, - data=args.data, - guardian=guardian_address, - relayer=relayer_address, - ) - - shared.set_options_for_guarded_transaction_if_needed(tx) - shared.set_options_for_hash_signing_if_needed(tx, sender, guardian, relayer) - - tx.signature = sender.sign_transaction(tx) - - shared.sign_guarded_transaction_if_guardian(tx, guardian, args.guardian_service_url, args.guardian_2fa_code) - shared.sign_relayed_transaction_if_relayer(tx, relayer) + validators.prepare_args_for_stake(args) + tx = do_prepare_transaction(args) cli_shared.send_or_simulate(tx, args) From a5137c34f0741589dbdf1a3467dc6bf5daaed486 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 5 Feb 2025 15:23:03 +0200 Subject: [PATCH 22/84] rename file --- .../{test_cli_validators.py => test_cli_validators_localnet.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename multiversx_sdk_cli/tests/{test_cli_validators.py => test_cli_validators_localnet.py} (100%) diff --git a/multiversx_sdk_cli/tests/test_cli_validators.py b/multiversx_sdk_cli/tests/test_cli_validators_localnet.py similarity index 100% rename from multiversx_sdk_cli/tests/test_cli_validators.py rename to multiversx_sdk_cli/tests/test_cli_validators_localnet.py From 9bf94dce4e47b46ff5d52877e682b23f8081fbb4 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 5 Feb 2025 17:00:47 +0200 Subject: [PATCH 23/84] add cli tests for validators --- .../tests/test_cli_validators.py | 542 ++++++++++++++++++ 1 file changed, 542 insertions(+) create mode 100644 multiversx_sdk_cli/tests/test_cli_validators.py diff --git a/multiversx_sdk_cli/tests/test_cli_validators.py b/multiversx_sdk_cli/tests/test_cli_validators.py new file mode 100644 index 00000000..b97d827f --- /dev/null +++ b/multiversx_sdk_cli/tests/test_cli_validators.py @@ -0,0 +1,542 @@ +import json +from pathlib import Path +from typing import Any + +from multiversx_sdk_cli.cli import main + +testdata_path = Path(__file__).parent / "testdata" +testdata_out = Path(__file__).parent / "testdata-out" + +alice_pem = testdata_path / "alice.pem" +reward_address = "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8" +bls_key = "e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208" + +relayer = testdata_path / "testUser.pem" +guardian = testdata_path / "testUser2.pem" + + +def test_stake(capsys: Any): + validators_json = testdata_path / "validators_file.json" + + return_code = main( + [ + "validator", + "stake", + "--pem", + str(alice_pem), + "--value", + "2500000000000000000000", + "--validators-file", + str(validators_json), + "--reward-address", + reward_address, + "--chain", + "localnet", + "--estimate-gas", + "--nonce=0", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "2500000000000000000000" + assert tx["nonce"] == 0 + assert tx["gasLimit"] == 11029500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "66c0a8334b4e9d2bcc87214762c2957c8b190c91db27f6bac43a9c309f5310a752d293796938c3c131e9e96836e00e62fabec64a1205b05822f634415f675909" + ) + assert ( + data + == "stake@02@F8910E47CF9464777C912E6390758BB39715FFFCB861B184017920E4A807B42553F2F21E7F3914B81BCF58B66A72AB16D97013AE1CFF807CEFC977EF8CBF116258534B9E46D19528042D16EF8374404A89B184E0A4EE18C77C49E454D04EAE8D@1865870F7F69162A2DFEFD33FE232A9CA984C6F22D1EE3F6A5B34A8EB8C9F7319001F29D5A2EED85C1500ACA19FA4189@1B4E60E6D100CDF234D3427494DAC55FBAC49856CADC86BCB13A01B9BB05A0D9143E86C186C948E7AE9E52427C9523102EFE9019A2A9C06DB02993F2E3E6756576AE5A3EC7C235D548BC79DE1A6990E1120AE435CB48F7FC436C9F9098B92A0D@12B309791213AAC8AD9F34F0D912261E30F9AB060859E4D515E020A98B91D82A7CD334E4B504BB93D6B75347CCCD6318@B2A11555CE521E4944E09AB17549D85B487DCD26C84B5017A39E31A3670889BA" + ) + + +def test_stake_with_relayer_and_guardian(capsys: Any): + validators_json = testdata_path / "validators_file.json" + + return_code = main( + [ + "validator", + "stake", + "--pem", + str(alice_pem), + "--value", + "2500000000000000000000", + "--validators-file", + str(validators_json), + "--reward-address", + reward_address, + "--chain", + "localnet", + "--estimate-gas", + "--nonce=0", + "--options=2", + "--relayer", + "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--guardian", + "erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4", + "--guardian-pem", + str(guardian), + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "2500000000000000000000" + assert tx["nonce"] == 0 + assert tx["gasLimit"] == 11029500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 2 + assert tx["guardian"] == "erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" + assert tx["relayer"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert ( + tx["signature"] + == "01bcfddf60d36c6f1fa6449640f3b4ed1755003b1c128b4d4b6e0a74f2c9edbee0e94a8f1058b9388cdbd97e7bd8fada3fc64bc7ed99f4239694f996b85f0108" + ) + assert ( + tx["guardianSignature"] + == "dfbd3d7ed19d607085779fef911e77ca63cfddb159aa3338bad9209064d50f90a24aeeac8082ec3b424afb3054189aab11467c016bb0ba945a43e191a9073306" + ) + assert ( + data + == "stake@02@F8910E47CF9464777C912E6390758BB39715FFFCB861B184017920E4A807B42553F2F21E7F3914B81BCF58B66A72AB16D97013AE1CFF807CEFC977EF8CBF116258534B9E46D19528042D16EF8374404A89B184E0A4EE18C77C49E454D04EAE8D@1865870F7F69162A2DFEFD33FE232A9CA984C6F22D1EE3F6A5B34A8EB8C9F7319001F29D5A2EED85C1500ACA19FA4189@1B4E60E6D100CDF234D3427494DAC55FBAC49856CADC86BCB13A01B9BB05A0D9143E86C186C948E7AE9E52427C9523102EFE9019A2A9C06DB02993F2E3E6756576AE5A3EC7C235D548BC79DE1A6990E1120AE435CB48F7FC436C9F9098B92A0D@12B309791213AAC8AD9F34F0D912261E30F9AB060859E4D515E020A98B91D82A7CD334E4B504BB93D6B75347CCCD6318@B2A11555CE521E4944E09AB17549D85B487DCD26C84B5017A39E31A3670889BA" + ) + + +def test_stake_top_up(capsys: Any): + return_code = main( + [ + "validator", + "stake", + "--top-up", + "--pem", + str(alice_pem), + "--value", + "2711000000000000000000", + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "2711000000000000000000" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5057500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "3af38033e7661311e2e180b29930271ad54c05716e13bb33bafdb89e48250db525c056a51a25faf8e5bba31f6ddc03ed48f9e0e79b5f3da5ccc9f0b0a9a83207" + ) + assert data == "stake" + + +def test_unstake(capsys: Any): + return_code = main( + [ + "validator", + "unstake", + "--pem", + str(alice_pem), + "--nodes-public-key", + bls_key, + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "0" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5350000 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "b387f3255670f17dbc4dd84bbed1f70631d2528f8f8ece7fb5c0fcc29a8b0b142583fe216c9de0086ebb69f9a50fc087ac4e5570fa2f61694df3b2cdb9389008" + ) + assert ( + data + == "unStake@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208" + ) + + +def test_unbond(capsys: Any): + return_code = main( + [ + "validator", + "unbond", + "--pem", + str(alice_pem), + "--nodes-public-key", + bls_key, + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "0" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5348500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "65c01d7c0ac26169d74d56612d1eab3a00c82b2f57af6b4aebbf39aa75e0f5e973d7daabb00bfad14d2f8bf63936ca9d69ef8fb76b8ac9c8ad0d2f808936930e" + ) + assert ( + data + == "unBond@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208" + ) + + +def test_unjail(capsys: Any): + return_code = main( + [ + "validator", + "unjail", + "--pem", + str(alice_pem), + "--value", + "2500000000000000000000", + "--nodes-public-key", + bls_key, + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "2500000000000000000000" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5348500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "5e682ba1e16b62971d3c6e6943ec954eb29fc31d8794d21afdd9e2c4ea5ba209a59cba8bd39c2a6ab9f80f066d558679e5b22bfca561caedbfa6a7297ad97d00" + ) + assert ( + data + == "unJail@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208" + ) + + +def test_change_reward_address(capsys: Any): + return_code = main( + [ + "validator", + "change-reward-address", + "--pem", + str(alice_pem), + "--reward-address", + reward_address, + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "0" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5176000 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "565e64ea28d48150e3798e20c0a0a0294374992ed05e5153e93339b3f86acdd25db308b474dd8315a544d6e375ed9e07b01c3475bdc7986fde79eae26bd5a40c" + ) + assert data == "changeRewardAddress@b2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba" + + +def test_unstake_nodes(capsys: Any): + return_code = main( + [ + "validator", + "unstake-nodes", + "--pem", + str(alice_pem), + "--nodes-public-key", + bls_key, + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "0" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5357500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "983b3127490949bc29e722a40a87871e88a6eb085fffb4d2801b2a79b39ff0aa395a39dc03367edb5f3858ed7ad9bea7b0c141110717cb639c45010055254606" + ) + assert ( + data + == "unStakeNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208" + ) + + +def test_unstake_tokens(capsys: Any): + return_code = main( + [ + "validator", + "unstake-tokens", + "--pem", + str(alice_pem), + "--unstake-value", + "11000000000000000000", + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "0" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5101000 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "c9506332641dc0a45a71b4f8e256f3761151173de73b0e2fbe627afa487911c6905911c74a45e04c947938eb30e1bc785cdae83cbbd35f248c4519b7a3e6800f" + ) + assert data == "unStakeTokens@000098a7d9b8314c0000" + + +def test_unbond_nodes(capsys: Any): + return_code = main( + [ + "validator", + "unbond-nodes", + "--pem", + str(alice_pem), + "--nodes-public-keys", + bls_key, + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "0" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5356000 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "ee261c7e7f1dc7822b31c609c570ba1b1da3e39ea68e231f2aea30ca9f70e0f61679f81739f22eee338fa9b8c14d498ca8892cde118d53b08f1440fe3737eb02" + ) + assert ( + data + == "unBondNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208" + ) + + +def test_unbond_tokens(capsys: Any): + return_code = main( + [ + "validator", + "unbond-tokens", + "--pem", + str(alice_pem), + "--unbond-value", + "20000000000000000000", + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "0" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5099500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "6eadd1d09700ba8fa8aedf01dcb6b44cb2551b59d65c2ab1ff8cac6bb0052a40a796b9ccb9d92439d6fdbd8536c7de5c5a28312ffef77ac9c1353f8236355206" + ) + assert data == "unBondTokens@0001158e460913d00000" + + +def test_clean_registration_data(capsys: Any): + return_code = main( + [ + "validator", + "clean-registered-data", + "--pem", + str(alice_pem), + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "0" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5078500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "005c35ccf2bbffbc753c8971aba1edffb43dbad1db62a88a26d295445937bb7f84dfd26e31329f8622ec9b53c5be4a39f1dd8ab83189f2cd5211c1c8541d7b00" + ) + assert data == "cleanRegisteredData" + + +def test_re_stake_unstaked_nodes(capsys: Any): + return_code = main( + [ + "validator", + "restake-unstaked-nodes", + "--pem", + str(alice_pem), + "--nodes-public-keys", + bls_key, + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "0" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5369500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "5f2196b81d9a72df401655becfc31e4167d89e76235f52abf506f9d9b10375b8b699339693b3f2d12552366e38ec2722a2ed50490a2beeaee0ae819d08f1ea0e" + ) + assert ( + data + == "reStakeUnStakedNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208" + ) + + +def get_output(capsys: Any): + tx = _read_stdout(capsys) + return json.loads(tx) + + +def _read_stdout(capsys: Any) -> str: + return capsys.readouterr().out.strip() From 0b32f5210eeea0f88387c51d4be6c35a85145ced Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 6 Feb 2025 16:21:27 +0200 Subject: [PATCH 24/84] refactoring validators transactions --- multiversx_sdk_cli/cli_validators.py | 506 ++++++++++- multiversx_sdk_cli/config.py | 2 + .../tests/test_cli_validators.py | 95 ++- multiversx_sdk_cli/validators/__init__.py | 31 - multiversx_sdk_cli/validators/core.py | 784 ++++++++++++++---- 5 files changed, 1170 insertions(+), 248 deletions(-) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 51e7f09c..17e8f9f3 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -1,7 +1,10 @@ +from pathlib import Path from typing import Any, List -from multiversx_sdk_cli import cli_shared, utils, validators -from multiversx_sdk_cli.transactions import do_prepare_transaction +from multiversx_sdk import Address + +from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.validators.core import ValidatorsController def setup_parser(args: List[str], subparsers: Any) -> Any: @@ -136,120 +139,547 @@ def _add_nodes_arg(sub: Any): def do_stake(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_stake(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + rewards_address = Address.new_from_bech32(args.reward_address) if args.reward_address else None + + controller = ValidatorsController(args.chain) + + if args.top_up: + tx = controller.create_transaction_for_topping_up( + sender=sender, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + else: + validators_file = Path(args.validators_file) + tx = controller.create_transaction_for_staking( + sender=sender, + validators_file=validators_file, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + rewards_address=rewards_address, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def do_unstake(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_unstake(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + keys = args.nodes_public_keys + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_unstaking( + sender=sender, + keys=keys, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def do_unjail(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_unjail(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + keys = args.nodes_public_keys + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_unjailing( + sender=sender, + keys=keys, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def do_unbond(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_unbond(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + keys = args.nodes_public_keys + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_unbonding( + sender=sender, + keys=keys, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def change_reward_address(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_change_reward_address(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + rewards_address = Address.new_from_bech32(args.reward_address) + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_changing_rewards_address( + sender=sender, + rewards_address=rewards_address, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def do_claim(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_claim(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_claiming( + sender=sender, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def do_unstake_nodes(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_unstake_nodes(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + + keys = args.nodes_public_keys + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_unstaking_nodes( + sender=sender, + keys=keys, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def do_unstake_tokens(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_unstake_tokens(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + value = int(args.unstake_value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_unstaking_tokens( + sender=sender, + value=value, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def do_unbond_nodes(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_unbond_nodes(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + + keys = args.nodes_public_keys + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_unbonding_nodes( + sender=sender, + keys=keys, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def do_unbond_tokens(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_unbond_tokens(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + value = int(args.unbond_value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_unbonding_tokens( + sender=sender, + value=value, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def do_clean_registered_data(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_clean_registered_data(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_cleaning_registered_data( + sender=sender, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def do_restake_unstaked_nodes(args: Any): + cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - validators.prepare_args_for_restake_unstaked_nodes(args) - tx = do_prepare_transaction(args) + + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + gas_limit = 0 if args.estimate_gas else args.gas_limit + keys = args.nodes_public_keys + + controller = ValidatorsController(args.chain) + tx = controller.create_transaction_for_restaking_unstaked_nodes( + sender=sender, + keys=keys, + native_amount=native_amount, + estimate_gas=args.estimate_gas, + gas_limit=gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index d5e61b52..67e55d24 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -24,6 +24,8 @@ class MetaChainSystemSCsCost: DELEGATION_OPS = 1000000 UNSTAKE_TOKENS = 5000000 UNBOND_TOKENS = 5000000 + CLEAN_REGISTERED_DATA = 5000000 + RE_STAKE_UNSTAKED_NODES = 5000000 def get_dependency_resolution(key: str) -> str: diff --git a/multiversx_sdk_cli/tests/test_cli_validators.py b/multiversx_sdk_cli/tests/test_cli_validators.py index b97d827f..1632c3b4 100644 --- a/multiversx_sdk_cli/tests/test_cli_validators.py +++ b/multiversx_sdk_cli/tests/test_cli_validators.py @@ -52,14 +52,53 @@ def test_stake(capsys: Any): assert tx["options"] == 0 assert ( tx["signature"] - == "66c0a8334b4e9d2bcc87214762c2957c8b190c91db27f6bac43a9c309f5310a752d293796938c3c131e9e96836e00e62fabec64a1205b05822f634415f675909" + == "e9dd1159bc55bde84872f0595c9e24b0b210deb8cb02a1df7d7b0e1277436d2043437ffeaff540e11200c00417cf5ce396b3154f968ad62ab4359fb05a493b0d" ) assert ( data - == "stake@02@F8910E47CF9464777C912E6390758BB39715FFFCB861B184017920E4A807B42553F2F21E7F3914B81BCF58B66A72AB16D97013AE1CFF807CEFC977EF8CBF116258534B9E46D19528042D16EF8374404A89B184E0A4EE18C77C49E454D04EAE8D@1865870F7F69162A2DFEFD33FE232A9CA984C6F22D1EE3F6A5B34A8EB8C9F7319001F29D5A2EED85C1500ACA19FA4189@1B4E60E6D100CDF234D3427494DAC55FBAC49856CADC86BCB13A01B9BB05A0D9143E86C186C948E7AE9E52427C9523102EFE9019A2A9C06DB02993F2E3E6756576AE5A3EC7C235D548BC79DE1A6990E1120AE435CB48F7FC436C9F9098B92A0D@12B309791213AAC8AD9F34F0D912261E30F9AB060859E4D515E020A98B91D82A7CD334E4B504BB93D6B75347CCCD6318@B2A11555CE521E4944E09AB17549D85B487DCD26C84B5017A39E31A3670889BA" + == "stake@02@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1865870f7f69162a2dfefd33fe232a9ca984c6f22d1ee3f6a5b34a8eb8c9f7319001f29d5a2eed85c1500aca19fa4189@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d@12b309791213aac8ad9f34f0d912261e30f9ab060859e4d515e020a98b91d82a7cd334e4b504bb93d6b75347cccd6318@b2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba" ) +def test_top_up(capsys: Any): + return_code = main( + [ + "validator", + "stake", + "--pem", + str(alice_pem), + "--value", + "2500000000000000000000", + "--top-up", + "--chain", + "localnet", + "--estimate-gas", + "--nonce=0", + "--reward-address", + "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "2500000000000000000000" + assert tx["nonce"] == 0 + assert tx["gasLimit"] == 5057500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "ca4ebd1b9c92b0479351e9f84b0394ce15f529f4a5c056ab2dd37b923d7af81cbb7bfc8fbbea571843a797e3382795c0419f69ab357acbd9611899d39e449107" + ) + assert data == "stake" + + def test_stake_with_relayer_and_guardian(capsys: Any): validators_json = testdata_path / "validators_file.json" @@ -106,15 +145,15 @@ def test_stake_with_relayer_and_guardian(capsys: Any): assert tx["relayer"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" assert ( tx["signature"] - == "01bcfddf60d36c6f1fa6449640f3b4ed1755003b1c128b4d4b6e0a74f2c9edbee0e94a8f1058b9388cdbd97e7bd8fada3fc64bc7ed99f4239694f996b85f0108" + == "9ecca226c3e5913906a5f22971a0d84bb7a8c652e309ffab4073dea8bbd88caccc2ad9e33b6929f16acdedb676ad574ad66d520abd5b398469fa0cff3fc7ca03" ) assert ( tx["guardianSignature"] - == "dfbd3d7ed19d607085779fef911e77ca63cfddb159aa3338bad9209064d50f90a24aeeac8082ec3b424afb3054189aab11467c016bb0ba945a43e191a9073306" + == "12c1ee05d34282555d85f7786dc0e7ffbce960de88fb75ba81a237bd1f2cc175f50ee42e60b2857bf2cd49d02de12a4017f1c95f14910fcc27bc7cb16b41ce04" ) assert ( data - == "stake@02@F8910E47CF9464777C912E6390758BB39715FFFCB861B184017920E4A807B42553F2F21E7F3914B81BCF58B66A72AB16D97013AE1CFF807CEFC977EF8CBF116258534B9E46D19528042D16EF8374404A89B184E0A4EE18C77C49E454D04EAE8D@1865870F7F69162A2DFEFD33FE232A9CA984C6F22D1EE3F6A5B34A8EB8C9F7319001F29D5A2EED85C1500ACA19FA4189@1B4E60E6D100CDF234D3427494DAC55FBAC49856CADC86BCB13A01B9BB05A0D9143E86C186C948E7AE9E52427C9523102EFE9019A2A9C06DB02993F2E3E6756576AE5A3EC7C235D548BC79DE1A6990E1120AE435CB48F7FC436C9F9098B92A0D@12B309791213AAC8AD9F34F0D912261E30F9AB060859E4D515E020A98B91D82A7CD334E4B504BB93D6B75347CCCD6318@B2A11555CE521E4944E09AB17549D85B487DCD26C84B5017A39E31A3670889BA" + == "stake@02@f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d@1865870f7f69162a2dfefd33fe232a9ca984c6f22d1ee3f6a5b34a8eb8c9f7319001f29d5a2eed85c1500aca19fa4189@1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d@12b309791213aac8ad9f34f0d912261e30f9ab060859e4d515e020a98b91d82a7cd334e4b504bb93d6b75347cccd6318@b2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba" ) @@ -310,6 +349,40 @@ def test_change_reward_address(capsys: Any): assert data == "changeRewardAddress@b2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba" +def test_claim(capsys: Any): + return_code = main( + [ + "validator", + "claim", + "--pem", + str(alice_pem), + "--chain", + "localnet", + "--estimate-gas", + "--nonce=7", + ] + ) + assert return_code == 0 + + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" + assert tx["value"] == "0" + assert tx["nonce"] == 7 + assert tx["gasLimit"] == 5057500 + assert tx["chainID"] == "localnet" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert ( + tx["signature"] + == "be19a2c0bf5ce1da5f72a7451bff57725161bb67a8b85e397d44570585e6b7ff40858b0d30fd9a12f06c70655b1c417389f25059e0a95dc76e488185cea68208" + ) + assert data == "claim" + + def test_unstake_nodes(capsys: Any): return_code = main( [ @@ -374,15 +447,15 @@ def test_unstake_tokens(capsys: Any): assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" assert tx["value"] == "0" assert tx["nonce"] == 7 - assert tx["gasLimit"] == 5101000 + assert tx["gasLimit"] == 5095000 assert tx["chainID"] == "localnet" assert tx["version"] == 2 assert tx["options"] == 0 assert ( tx["signature"] - == "c9506332641dc0a45a71b4f8e256f3761151173de73b0e2fbe627afa487911c6905911c74a45e04c947938eb30e1bc785cdae83cbbd35f248c4519b7a3e6800f" + == "ed8e401e875d70bc3a62bf966fc8a9ecda2d49a851fe216f265176be5ab43040a85df55798dc828c928079573e2aa8dc52627e87c92824d8c91fdc3f3d195e0a" ) - assert data == "unStakeTokens@000098a7d9b8314c0000" + assert data == "unStakeTokens@98a7d9b8314c0000" def test_unbond_nodes(capsys: Any): @@ -449,15 +522,15 @@ def test_unbond_tokens(capsys: Any): assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" assert tx["value"] == "0" assert tx["nonce"] == 7 - assert tx["gasLimit"] == 5099500 + assert tx["gasLimit"] == 5096500 assert tx["chainID"] == "localnet" assert tx["version"] == 2 assert tx["options"] == 0 assert ( tx["signature"] - == "6eadd1d09700ba8fa8aedf01dcb6b44cb2551b59d65c2ab1ff8cac6bb0052a40a796b9ccb9d92439d6fdbd8536c7de5c5a28312ffef77ac9c1353f8236355206" + == "a7c96028a97d035c0068b9c2a4bbc4ee3b9613d81dfa4c388fd8a90e66f4e200e715e87a760c0a7d456f3b3a4dc225f760084477cb15ac690c9e1cb7c006f70d" ) - assert data == "unBondTokens@0001158e460913d00000" + assert data == "unBondTokens@01158e460913d00000" def test_clean_registration_data(capsys: Any): diff --git a/multiversx_sdk_cli/validators/__init__.py b/multiversx_sdk_cli/validators/__init__.py index 1c7ba1f4..e69de29b 100644 --- a/multiversx_sdk_cli/validators/__init__.py +++ b/multiversx_sdk_cli/validators/__init__.py @@ -1,31 +0,0 @@ -from multiversx_sdk_cli.validators.core import ( - prepare_args_for_change_reward_address, - prepare_args_for_claim, - prepare_args_for_clean_registered_data, - prepare_args_for_restake_unstaked_nodes, - prepare_args_for_stake, - prepare_args_for_unbond, - prepare_args_for_unbond_nodes, - prepare_args_for_unbond_tokens, - prepare_args_for_unjail, - prepare_args_for_unstake, - prepare_args_for_unstake_nodes, - prepare_args_for_unstake_tokens, -) -from multiversx_sdk_cli.validators.validators_file import ValidatorsFile - -__all__ = [ - "prepare_args_for_stake", - "prepare_args_for_unstake", - "prepare_args_for_unbond", - "prepare_args_for_unjail", - "prepare_args_for_change_reward_address", - "prepare_args_for_claim", - "prepare_args_for_unstake_nodes", - "prepare_args_for_unstake_tokens", - "prepare_args_for_unbond_nodes", - "prepare_args_for_unbond_tokens", - "prepare_args_for_clean_registered_data", - "prepare_args_for_restake_unstaked_nodes", - "ValidatorsFile", -] diff --git a/multiversx_sdk_cli/validators/core.py b/multiversx_sdk_cli/validators/core.py index de37b597..a94b7bce 100644 --- a/multiversx_sdk_cli/validators/core.py +++ b/multiversx_sdk_cli/validators/core.py @@ -1,13 +1,21 @@ import logging from pathlib import Path -from typing import Any, List, Tuple, Union - -from multiversx_sdk import Address, ValidatorPEM, ValidatorSigner - -from multiversx_sdk_cli import cli_shared, utils +from typing import Any, Optional, Union + +from multiversx_sdk import Address, Transaction +from multiversx_sdk.abi import ( + AddressValue, + BigUIntValue, + BytesValue, + Serializer, + U32Value, +) + +from multiversx_sdk_cli import utils from multiversx_sdk_cli.config import MetaChainSystemSCsCost, get_address_hrp from multiversx_sdk_cli.constants import GAS_PER_DATA_BYTE, MIN_GAS_LIMIT -from multiversx_sdk_cli.contracts import prepare_execute_transaction_data +from multiversx_sdk_cli.interfaces import IAccount +from multiversx_sdk_cli.transactions import TransactionsController from multiversx_sdk_cli.validators.validators_file import ValidatorsFile logger = logging.getLogger("validators") @@ -15,165 +23,605 @@ VALIDATORS_SMART_CONTRACT_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000001ffff" -def prepare_args_for_stake(args: Any): - if args.top_up: - prepare_args_for_top_up(args) - return - - node_operator = cli_shared.prepare_account(args) - - validators_file_path = Path(args.validators_file) - reward_address = Address.new_from_bech32(args.reward_address) if args.reward_address else None - - data, gas_limit = prepare_transaction_data_for_stake(node_operator.address, validators_file_path, reward_address) - args.data = data - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - - if args.estimate_gas: - args.gas_limit = gas_limit - - -def prepare_transaction_data_for_stake( - node_operator_address: Address, - validators_file_path: Path, - reward_address: Union[Address, None], -) -> Tuple[str, int]: - validators_file = ValidatorsFile(validators_file_path) - num_of_nodes = validators_file.get_num_of_nodes() - validators_list = validators_file.get_validators_list() - - call_arguments: List[Any] = [] - call_arguments.append(num_of_nodes) - - for validator in validators_list: - # Get path of "pemFile", make it absolute - validator_pem = Path(validator.get("pemFile")).expanduser() - validator_pem = validator_pem if validator_pem.is_absolute() else validators_file_path.parent / validator_pem - - pem_file = ValidatorPEM.from_file(validator_pem) - - validator_signer = ValidatorSigner(pem_file.secret_key) - signed_message = validator_signer.sign(node_operator_address.pubkey).hex() - - call_arguments.append(f"0x{pem_file.secret_key.generate_public_key().hex()}") - call_arguments.append(f"0x{signed_message}") - - if reward_address: - call_arguments.append(f"0x{reward_address.to_hex()}") - - data = prepare_execute_transaction_data("stake", call_arguments) - gas_limit = estimate_system_sc_call(str(data), MetaChainSystemSCsCost.STAKE, num_of_nodes) - - return str(data), gas_limit - - -def prepare_args_for_top_up(args: Any): - args.data = "stake" - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.STAKE, 1) - - -def prepare_args_for_unstake(args: Any): - parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = "unStake" + parsed_keys - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNSTAKE, num_keys) - - -def prepare_args_for_unbond(args: Any): - parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = "unBond" + parsed_keys - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNBOND, num_keys) - - -def prepare_args_for_unjail(args: Any): - parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = "unJail" + parsed_keys - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNJAIL, num_keys) - - -def prepare_args_for_change_reward_address(args: Any): - reward_address = Address.new_from_bech32(args.reward_address) - args.data = "changeRewardAddress@" + reward_address.hex() - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.CHANGE_REWARD_ADDRESS) - - -def prepare_args_for_claim(args: Any): - args.data = "claim" - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.CLAIM) - - -def prepare_args_for_unstake_nodes(args: Any): - parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = "unStakeNodes" + parsed_keys - - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNSTAKE, num_keys) - - -def prepare_args_for_unstake_tokens(args: Any): - args.data = "unStakeTokens" - args.data += "@" + utils.str_int_to_hex_str(str(args.unstake_value)) - - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNSTAKE_TOKENS) - - -def prepare_args_for_unbond_nodes(args: Any): - parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = "unBondNodes" + parsed_keys - - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNBOND, num_keys) - - -def prepare_args_for_unbond_tokens(args: Any): - args.data = "unBondTokens" - args.data += "@" + utils.str_int_to_hex_str(str(args.unbond_value)) - - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNBOND_TOKENS) - - -def prepare_args_for_clean_registered_data(args: Any): - args.data = "cleanRegisteredData" - - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.STAKE) - - -def prepare_args_for_restake_unstaked_nodes(args: Any): - parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) - args.data = "reStakeUnStakedNodes" + parsed_keys - - args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() - if args.estimate_gas: - args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.STAKE, num_keys) - - -def estimate_system_sc_call(transaction_data: str, base_cost: int, factor: int = 1): - num_bytes = len(transaction_data) - gas_limit = MIN_GAS_LIMIT + num_bytes * GAS_PER_DATA_BYTE - gas_limit += factor * base_cost - return gas_limit +class ValidatorsController: + def __init__(self, chain_id: str) -> None: + self.transactions_controller = TransactionsController(chain_id) + self.serializer = Serializer() + + def create_transaction_for_staking( + self, + sender: IAccount, + validators_file: Path, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + rewards_address: Optional[Address] = None, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + validators = ValidatorsFile(validators_file) + data = self.prepare_transaction_data_for_stake( + node_operator=sender.address, + validators_file=validators, + rewards_address=rewards_address, + ) + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.STAKE, validators.get_num_of_nodes()) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def prepare_transaction_data_for_stake( + self, + node_operator: Address, + validators_file: ValidatorsFile, + rewards_address: Union[Address, None], + ) -> str: + num_of_nodes = validators_file.get_num_of_nodes() + + call_arguments: list[Any] = [] + call_arguments.append(U32Value(num_of_nodes)) + + validator_signers = validators_file.load_signers() + + for validator in validator_signers: + signed_message = validator.sign(node_operator.get_public_key()) + + call_arguments.append(BytesValue(validator.secret_key.generate_public_key().buffer)) + call_arguments.append(BytesValue(signed_message)) + + if rewards_address: + call_arguments.append(AddressValue.new_from_address(rewards_address)) + + data = "stake@" + self.serializer.serialize(call_arguments) + return data + + def create_transaction_for_topping_up( + self, + sender: IAccount, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + data = "stake" + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.STAKE, 1) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_unstaking( + self, + sender: IAccount, + keys: str, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + parsed_keys, num_keys = utils.parse_keys(keys) + data = f"unStake{parsed_keys}" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNSTAKE, num_keys) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_unjailing( + self, + sender: IAccount, + keys: str, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + parsed_keys, num_keys = utils.parse_keys(keys) + data = f"unJail{parsed_keys}" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNJAIL, num_keys) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_unbonding( + self, + sender: IAccount, + keys: str, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + parsed_keys, num_keys = utils.parse_keys(keys) + data = f"unBond{parsed_keys}" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNBOND, num_keys) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_changing_rewards_address( + self, + sender: IAccount, + rewards_address: Address, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + data = f"changeRewardAddress@{rewards_address.to_hex()}" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.CHANGE_REWARD_ADDRESS) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_claiming( + self, + sender: IAccount, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + data = "claim" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.CLAIM) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_unstaking_nodes( + self, + sender: IAccount, + keys: str, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + parsed_keys, num_keys = utils.parse_keys(keys) + data = f"unStakeNodes{parsed_keys}" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNSTAKE, num_keys) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_unstaking_tokens( + self, + sender: IAccount, + value: int, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + data = f"unStakeTokens@{self.serializer.serialize([BigUIntValue(value)])}" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNSTAKE_TOKENS) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_unbonding_nodes( + self, + sender: IAccount, + keys: str, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + parsed_keys, num_keys = utils.parse_keys(keys) + data = f"unBondNodes{parsed_keys}" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNBOND, num_keys) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_unbonding_tokens( + self, + sender: IAccount, + value: int, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + data = f"unBondTokens@{self.serializer.serialize([BigUIntValue(value)])}" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNBOND_TOKENS) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_cleaning_registered_data( + self, + sender: IAccount, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + data = "cleanRegisteredData" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.CLEAN_REGISTERED_DATA) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def create_transaction_for_restaking_unstaked_nodes( + self, + sender: IAccount, + keys: str, + native_amount: int, + estimate_gas: bool, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + parsed_keys, num_keys = utils.parse_keys(keys) + data = f"reStakeUnStakedNodes{parsed_keys}" + + if estimate_gas: + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.RE_STAKE_UNSTAKED_NODES, num_keys) + + receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) + + return self.transactions_controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limt=gas_limit, + gas_price=gas_price, + nonce=nonce, + version=version, + options=options, + data=data, + guardian_account=guardian_account, + guardian_address=guardian_address, + relayer_account=relayer_account, + relayer_address=relayer_address, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + def estimate_system_sc_call(self, transaction_data: str, base_cost: int, factor: int = 1): + num_bytes = len(transaction_data) + gas_limit = MIN_GAS_LIMIT + num_bytes * GAS_PER_DATA_BYTE + gas_limit += factor * base_cost + return gas_limit From 5cd64b6312faa4cfb9804e984a02f52f62be6b2b Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 6 Feb 2025 17:05:56 +0200 Subject: [PATCH 25/84] remove unused code --- multiversx_sdk_cli/contracts.py | 71 ++----------------- multiversx_sdk_cli/tests/test_contracts.py | 37 +--------- .../tests/test_validators_core.py | 21 ------ 3 files changed, 6 insertions(+), 123 deletions(-) delete mode 100644 multiversx_sdk_cli/tests/test_validators_core.py diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 947788cd..43c9e7b5 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -280,71 +280,10 @@ def _prepare_args_for_factory(self, arguments: list[str]) -> list[Any]: def _hex_to_bytes(self, arg: str): argument = arg[len(HEX_PREFIX) :] argument = argument.upper() - argument = ensure_even_length(argument) + argument = self.ensure_even_length(argument) return bytes.fromhex(argument) - -def prepare_execute_transaction_data(function: str, arguments: list[Any]) -> str: - tx_data = function - - for arg in arguments: - tx_data += f"@{_prepare_argument(arg)}" - - return tx_data - - -# only used for stake operations -def _prepare_argument(argument: Any): - as_str = str(argument) - as_hex = _to_hex(as_str) - return as_hex - - -def _to_hex(arg: str): - if arg.startswith(HEX_PREFIX): - return _prepare_hexadecimal(arg) - - if arg.isnumeric(): - return _prepare_decimal(arg) - elif arg.startswith(get_address_hrp()): - addr = Address.from_bech32(arg) - return _prepare_hexadecimal(f"{HEX_PREFIX}{addr.hex()}") - elif arg.lower() == FALSE_STR_LOWER or arg.lower() == TRUE_STR_LOWER: - as_str = f"{HEX_PREFIX}01" if arg.lower() == TRUE_STR_LOWER else f"{HEX_PREFIX}00" - return _prepare_hexadecimal(as_str) - elif arg.startswith(STR_PREFIX): - as_hex = f"{HEX_PREFIX}{arg[len(STR_PREFIX):].encode('ascii').hex()}" - return _prepare_hexadecimal(as_hex) - else: - raise Exception(f"could not convert {arg} to hex") - - -def _prepare_hexadecimal(argument: str) -> str: - argument = argument[len(HEX_PREFIX) :] - argument = argument.upper() - argument = ensure_even_length(argument) - - if argument == "": - return "" - - try: - _ = int(argument, 16) - except ValueError: - raise errors.UnknownArgumentFormat(argument) - return argument - - -def _prepare_decimal(argument: str) -> str: - if not argument.isnumeric(): - raise errors.UnknownArgumentFormat(argument) - - as_number = int(argument) - as_hexstring = hex(as_number)[len(HEX_PREFIX) :] - as_hexstring = ensure_even_length(as_hexstring) - return as_hexstring.upper() - - -def ensure_even_length(string: str) -> str: - if len(string) % 2 == 1: - return "0" + string - return string + def ensure_even_length(self, string: str) -> str: + if len(string) % 2 == 1: + return "0" + string + return string diff --git a/multiversx_sdk_cli/tests/test_contracts.py b/multiversx_sdk_cli/tests/test_contracts.py index ef312959..d81cad3f 100644 --- a/multiversx_sdk_cli/tests/test_contracts.py +++ b/multiversx_sdk_cli/tests/test_contracts.py @@ -1,13 +1,11 @@ import logging from pathlib import Path -import pytest from Cryptodome.Hash import keccak from multiversx_sdk import Account, Address, TransactionsFactoryConfig -from multiversx_sdk_cli import errors from multiversx_sdk_cli.contract_verification import _create_request_signature -from multiversx_sdk_cli.contracts import SmartContract, _prepare_argument +from multiversx_sdk_cli.contracts import SmartContract logging.basicConfig(level=logging.INFO) @@ -19,39 +17,6 @@ def test_playground_keccak(): assert hexhash == "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" -def test_prepare_argument(): - assert _prepare_argument("0x5") == "05" - assert _prepare_argument("5") == "05" - assert _prepare_argument("0x05f") == "005F" - assert _prepare_argument("0xaaa") == "0AAA" - assert _prepare_argument("str:a") == "61" - assert _prepare_argument("str:aaa") == "616161" - - assert _prepare_argument(155) == "9B" - assert _prepare_argument("155") == "9B" - - assert ( - _prepare_argument("erd1qr9av6ar4ymr05xj93jzdxyezdrp6r4hz6u0scz4dtzvv7kmlldse7zktc") - == "00CBD66BA3A93637D0D22C6426989913461D0EB716B8F860556AC4C67ADBFFDB" - ) - - assert _prepare_argument("str:TOK-123456") == "544F4B2D313233343536" - assert _prepare_argument("str:TOK-a1c2ef") == "544F4B2D613163326566" - assert _prepare_argument("str:TokenName") == "546F6B656E4E616D65" - assert _prepare_argument("str:/#%placeholder&*") == "2F2325706C616365686F6C646572262A" - - assert _prepare_argument(True) == "01" - assert _prepare_argument(False) == "00" - assert _prepare_argument("TrUe") == "01" - assert _prepare_argument("fAlSe") == "00" - - with pytest.raises(errors.UnknownArgumentFormat): - _ = _prepare_argument("0x05fq") - - assert _prepare_argument("str:") == "" - assert _prepare_argument("0x") == "" - - def test_contract_verification_create_request_signature(): account = Account.new_from_pem(file_path=testdata_folder / "walletKey.pem") contract_address = Address.from_bech32("erd1qqqqqqqqqqqqqpgqeyj9g344pqguukajpcfqz9p0rfqgyg4l396qespdck") diff --git a/multiversx_sdk_cli/tests/test_validators_core.py b/multiversx_sdk_cli/tests/test_validators_core.py deleted file mode 100644 index b382e2bb..00000000 --- a/multiversx_sdk_cli/tests/test_validators_core.py +++ /dev/null @@ -1,21 +0,0 @@ -from pathlib import Path - -from multiversx_sdk import Address - -from multiversx_sdk_cli.validators.core import prepare_transaction_data_for_stake - -TESTDATA_FOLDER = Path(__file__).parent.joinpath("testdata") - - -def test_prepare_transaction_data_for_stake(): - node_operator_address = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") - validators_file_path = TESTDATA_FOLDER / "validators.json" - data, gas_limit = prepare_transaction_data_for_stake( - node_operator_address, validators_file_path, node_operator_address - ) - - assert ( - data - == "stake@03@E7BEAA95B3877F47348DF4DD1CB578A4F7CABF7A20BFEEFE5CDD263878FF132B765E04FEF6F40C93512B666C47ED7719B8902F6C922C04247989B7137E837CC81A62E54712471C97A2DDAB75AA9C2F58F813ED4C0FA722BDE0AB718BFF382208@604882237A9845F508AD03877B5AAB90569683EEB51FAFCBBEB87440BA359992B3C0B837A8757C25BE18132549404F88@78689FD4B1E2E434D567FE01E61598A42717D83124308266BD09CCC15D2339DD318C019914B86AC29ADBAE5DD8A02D0307425E9BD85A296E94943708C72F8C670F0B7C50A890A5719088DBD9F1D062CAD9ACFFA06DF834106EEBE1A4257EF00D@EC54A009695AF56C3585EF623387B67B6DF1974B0B3C9138EB64BDE6EB33978AE9851112B20C99BF63588E8E949E4388@7188B234A8BF834F2E6258012AA09A2AB93178FFAB9C789480275F61FE02CD1B9A58DDC63B79A73ABEA9E2B7AC5CAC0B0D4324EFF50ACA2F0EC946B9AE6797511FA3CE461B57E77129CBA8AB3B51147695D4CE889CBE67905F6586B4E4F22491@C6C637DE17DB5F89A2FA1D1D935CB60C0E5E8958D3BFC47F903F774DD97398C8FE22093E113865EE98C3AFDD1DE62694@0139472EFF6886771A982F3083DA5D421F24C29181E63888228DC81CA60D69E1" - ) - assert gas_limit == 16464500 From baf67d12b75daea82df8be7489c14d2d236d866a Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 7 Feb 2025 11:04:21 +0200 Subject: [PATCH 26/84] fix typo --- multiversx_sdk_cli/cli_transactions.py | 2 +- multiversx_sdk_cli/transactions.py | 6 +++--- multiversx_sdk_cli/validators/core.py | 26 +++++++++++++------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 8c0608ad..ebbcc8d7 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -151,7 +151,7 @@ def create_transaction(args: Any): sender=sender, receiver=Address.new_from_bech32(args.receiver), native_amount=native_amount, - gas_limt=args.gas_limit, + gas_limit=args.gas_limit, gas_price=args.gas_price, nonce=nonce, version=args.version, diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 1cc478f2..a5a2374a 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -48,7 +48,7 @@ def create_transaction( sender: IAccount, receiver: Address, native_amount: int, - gas_limt: int, + gas_limit: int, gas_price: int, nonce: int, version: int, @@ -67,7 +67,7 @@ def create_transaction( transaction = Transaction( sender=sender.address, receiver=receiver, - gas_limit=gas_limt, + gas_limit=gas_limit, chain_id=self.factory.config.chain_id, ) else: @@ -79,7 +79,7 @@ def create_transaction( data=data.encode() if data else None, ) - transaction.gas_limit = gas_limt + transaction.gas_limit = gas_limit transaction.gas_price = gas_price transaction.nonce = nonce transaction.version = version diff --git a/multiversx_sdk_cli/validators/core.py b/multiversx_sdk_cli/validators/core.py index a94b7bce..7a8df43d 100644 --- a/multiversx_sdk_cli/validators/core.py +++ b/multiversx_sdk_cli/validators/core.py @@ -63,7 +63,7 @@ def create_transaction_for_staking( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -129,7 +129,7 @@ def create_transaction_for_topping_up( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -173,7 +173,7 @@ def create_transaction_for_unstaking( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -217,7 +217,7 @@ def create_transaction_for_unjailing( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -261,7 +261,7 @@ def create_transaction_for_unbonding( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -304,7 +304,7 @@ def create_transaction_for_changing_rewards_address( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -346,7 +346,7 @@ def create_transaction_for_claiming( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -390,7 +390,7 @@ def create_transaction_for_unstaking_nodes( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -433,7 +433,7 @@ def create_transaction_for_unstaking_tokens( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -477,7 +477,7 @@ def create_transaction_for_unbonding_nodes( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -520,7 +520,7 @@ def create_transaction_for_unbonding_tokens( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -562,7 +562,7 @@ def create_transaction_for_cleaning_registered_data( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, @@ -606,7 +606,7 @@ def create_transaction_for_restaking_unstaked_nodes( sender=sender, receiver=receiver, native_amount=native_amount, - gas_limt=gas_limit, + gas_limit=gas_limit, gas_price=gas_price, nonce=nonce, version=version, From db47be56138015f6ad8a1d210a14b7568f203b22 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 7 Feb 2025 11:05:13 +0200 Subject: [PATCH 27/84] refactor dns register --- multiversx_sdk_cli/dns.py | 51 +++++++++++++++++++----- multiversx_sdk_cli/tests/test_cli_dns.py | 37 ++++++++++++++++- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 3bacd1b4..527f1d7b 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -10,11 +10,11 @@ TransactionsFactoryConfig, ) -from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX from multiversx_sdk_cli.contracts import SmartContract -from multiversx_sdk_cli.transactions import do_prepare_transaction +from multiversx_sdk_cli.transactions import TransactionsController MaxNumShards = 256 ShardIdentiferLen = 2 @@ -72,16 +72,47 @@ def validate_name(name: str, shard_id: int, proxy: INetworkProvider): def register(args: Any): - args = utils.as_object(args) - cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) - cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) - args.receiver = dns_address_for_name(args.name).bech32() - args.data = dns_register_data(args.name) - tx = do_prepare_transaction(args) + sender = cli_shared.prepare_account(args) + + if args.nonce is None: + nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + else: + nonce = int(args.nonce) + + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + + native_amount = int(args.value) + + receiver = dns_address_for_name(args.name) + data = dns_register_data(args.name) + + controller = TransactionsController(args.chain) + + tx = controller.create_transaction( + sender=sender, + receiver=receiver, + native_amount=native_amount, + gas_limit=args.gas_limit, + gas_price=args.gas_price, + nonce=nonce, + version=args.version, + options=args.options, + data=data, + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) @@ -143,8 +174,8 @@ def compute_dns_address_for_shard_id(shard_id: int) -> Address: def dns_register_data(name: str) -> str: - name_enc: bytes = str.encode(name) - return "register@{}".format(name_enc.hex()) + name_as_hex = str.encode(name).hex() + return f"register@{name_as_hex}" def _query_contract(contract_address: Address, proxy: INetworkProvider, function: str, args: List[Any]) -> List[Any]: diff --git a/multiversx_sdk_cli/tests/test_cli_dns.py b/multiversx_sdk_cli/tests/test_cli_dns.py index 21da2b60..63aacccc 100644 --- a/multiversx_sdk_cli/tests/test_cli_dns.py +++ b/multiversx_sdk_cli/tests/test_cli_dns.py @@ -1,11 +1,13 @@ +import json from pathlib import Path +from typing import Any from multiversx_sdk_cli.cli import main testdata_path = Path(__file__).parent / "testdata" -def test_prepare_relayed_dns_register_transaction(): +def test_prepare_relayed_dns_register_transaction(capsys: Any): alice = testdata_path / "alice.pem" user = testdata_path / "testUser.pem" @@ -29,5 +31,36 @@ def test_prepare_relayed_dns_register_transaction(): str(user), ] ) + assert not return_code - assert False if return_code else True + output = get_output(capsys) + tx = output["emittedTransaction"] + data = output["emittedTransactionData"] + + assert tx["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx["receiver"] == "erd1qqqqqqqqqqqqqpgqf97pgqdy0tstwauxu09kszz020hp5kgqqzzsscqtww" + assert tx["value"] == "0" + assert tx["nonce"] == 0 + assert tx["gasLimit"] == 15000000 + assert tx["chainID"] == "T" + assert tx["version"] == 2 + assert tx["options"] == 0 + assert tx["relayer"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert ( + tx["signature"] + == "25af35896b853ad13bf1adcc3e516f8d7532349b4b00e958e30a6b9c0b9c38cbe0ce712684aba91e3f91bf080f6ae89d8cdfd6d0c69701d3761346aa6c54ac0d" + ) + assert ( + tx["relayerSignature"] + == "e7b22c3f8e3cfa8f15038d3b59beabe3e4b2a0e40fdb40e57c39e762450ebe3cdf327bb66585c27e66480846b7487d5e78366959f6f09f10bb63e9b643c08f03" + ) + assert data == "register@616c6963652e656c726f6e64" + + +def get_output(capsys: Any): + tx = _read_stdout(capsys) + return json.loads(tx) + + +def _read_stdout(capsys: Any) -> str: + return capsys.readouterr().out.strip() From 73e0da082428c161425d94657573d89d404525f5 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 7 Feb 2025 13:42:22 +0200 Subject: [PATCH 28/84] remove old & unused code --- multiversx_sdk_cli/dns.py | 8 +- multiversx_sdk_cli/transactions.py | 178 +----------------- .../validators/validators_file.py | 11 +- 3 files changed, 11 insertions(+), 186 deletions(-) diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 527f1d7b..38229b13 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional, Protocol, Union +from typing import Any, Optional, Protocol, Union from Cryptodome.Hash import keccak from multiversx_sdk import ( @@ -117,8 +117,8 @@ def register(args: Any): cli_shared.send_or_simulate(tx, args) -def compute_all_dns_addresses() -> List[Address]: - addresses: List[Address] = [] +def compute_all_dns_addresses() -> list[Address]: + addresses: list[Address] = [] for i in range(0, 256): addresses.append(compute_dns_address_for_shard_id(i)) return addresses @@ -178,7 +178,7 @@ def dns_register_data(name: str) -> str: return f"register@{name_as_hex}" -def _query_contract(contract_address: Address, proxy: INetworkProvider, function: str, args: List[Any]) -> List[Any]: +def _query_contract(contract_address: Address, proxy: INetworkProvider, function: str, args: list[Any]) -> list[Any]: chain_id = proxy.get_network_config().chain_id config = TransactionsFactoryConfig(chain_id) contract = SmartContract(config) diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index a5a2374a..fb5bf164 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -1,28 +1,19 @@ import json import logging import time -from pathlib import Path -from typing import Any, List, Optional, Protocol, TextIO, Union +from typing import Optional, Protocol, TextIO, Union from multiversx_sdk import ( - Account, Address, - LedgerAccount, - Token, - TokenComputer, TokenTransfer, Transaction, - TransactionComputer, TransactionOnNetwork, TransactionsFactoryConfig, TransferTransactionsFactory, ) -from multiversx_sdk_cli import config, errors +from multiversx_sdk_cli import errors from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController -from multiversx_sdk_cli.cli_password import load_guardian_password, load_password -from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.errors import IncorrectWalletError, NoWalletProvided from multiversx_sdk_cli.interfaces import IAccount logger = logging.getLogger("transactions") @@ -99,171 +90,6 @@ def create_transaction( return transaction -def do_prepare_transaction(args: Any) -> Transaction: - account = load_sender_account_from_args(args) - - native_amount = int(args.value) - transfers = getattr(args, "token_transfers", []) - transfers = prepare_token_transfers(transfers) if transfers else None - - config = TransactionsFactoryConfig(args.chain) - factory = TransferTransactionsFactory(config) - receiver = Address.new_from_bech32(args.receiver) - - if native_amount or transfers: - tx = factory.create_transaction_for_transfer( - sender=account.address, - receiver=receiver, - native_amount=native_amount, - token_transfers=transfers, - data=str(args.data).encode(), - ) - else: - # this is for transactions with no token transfers(egld/esdt); useful for setting the data field - tx = Transaction( - sender=account.address, - receiver=receiver, - data=str(args.data).encode(), - gas_limit=int(args.gas_limit), - chain_id=args.chain, - ) - - tx.gas_limit = int(args.gas_limit) - tx.sender_username = getattr(args, "sender_username", None) or "" - tx.receiver_username = getattr(args, "receiver_username", None) or "" - tx.gas_price = int(args.gas_price) - tx.nonce = int(args.nonce) - tx.value = int(args.value) - tx.version = int(args.version) - tx.options = int(args.options) - - tx_computer = TransactionComputer() - if isinstance(account, LedgerAccount): - tx_computer.apply_options_for_hash_signing(tx) - - if args.guardian: - tx.guardian = Address.new_from_bech32(args.guardian) - - if args.relayer: - tx.relayer = Address.new_from_bech32(args.relayer) - - try: - relayer_account = load_relayer_account_from_args(args) - if relayer_account.address != tx.relayer: - raise IncorrectWalletError("") - - if isinstance(relayer_account, LedgerAccount): - tx_computer.apply_options_for_hash_signing(tx) - - tx.relayer_signature = relayer_account.sign_transaction(tx) - except NoWalletProvided: - logger.warning("Relayer wallet not provided. Transaction will not be signed by relayer.") - except IncorrectWalletError: - raise IncorrectWalletError("Relayer wallet does not match the relayer's address set in the transaction.") - - try: - guardian_account = get_guardian_account_from_args(args) - if isinstance(guardian_account, LedgerAccount): - tx_computer.apply_options_for_hash_signing(tx) - - except NoWalletProvided: - guardian_account = None - - tx.signature = account.sign_transaction(tx) - tx = sign_tx_by_guardian(args, tx, guardian_account) - - return tx - - -def load_sender_account_from_args(args: Any) -> IAccount: - hrp = config.get_address_hrp() - - if args.pem: - return Account.new_from_pem(file_path=Path(args.pem), index=args.pem_index, hrp=hrp) - elif args.keyfile: - password = load_password(args) - account = Account.new_from_keystore( - file_path=Path(args.keyfile), - password=password, - address_index=args.address_index, - hrp=hrp, - ) - elif args.ledger: - account = LedgerAccount(address_index=args.ledger_address_index) - else: - raise errors.NoWalletProvided() - - return account - - -def load_relayer_account_from_args(args: Any) -> IAccount: - hrp = config.get_address_hrp() - - if args.relayer_ledger: - account = LedgerAccount(address_index=args.relayer_ledger_address_index) - if args.relayer_pem: - account = Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_pem_index, hrp=hrp) - elif args.relayer_keyfile: - password = load_password(args) - account = Account.new_from_keystore( - file_path=Path(args.relayer_keyfile), - password=password, - address_index=args.relayer_address_index, - hrp=hrp, - ) - else: - raise errors.NoWalletProvided() - - return account - - -def prepare_token_transfers(transfers: List[Any]) -> List[TokenTransfer]: - token_computer = TokenComputer() - token_transfers: List[TokenTransfer] = [] - - for i in range(0, len(transfers) - 1, 2): - identifier = transfers[i] - amount = int(transfers[i + 1]) - nonce = token_computer.extract_nonce_from_extended_identifier(identifier) - - token = Token(identifier, nonce) - transfer = TokenTransfer(token, amount) - token_transfers.append(transfer) - - return token_transfers - - -def sign_tx_by_guardian(args: Any, tx: Transaction, guardian_account: Union[IAccount, None]) -> Transaction: - if guardian_account: - tx.guardian_signature = guardian_account.sign_transaction(tx) - elif args.guardian: - cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) - - return tx - - -# TODO: this is duplicated code; a proper refactoring will come later -def get_guardian_account_from_args(args: Any) -> IAccount: - hrp = config.get_address_hrp() - - if args.guardian_pem: - account = Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_pem_index, hrp=hrp) - elif args.guardian_keyfile: - password = load_guardian_password(args) - account = Account.new_from_keystore( - file_path=Path(args.guardian_keyfile), - password=password, - address_index=args.guardian_address_index, - hrp=hrp, - ) - elif args.guardian_ledger: - account = LedgerAccount(address_index=args.relayer_ledger_address_index) - else: - raise errors.NoWalletProvided() - - return account - - def send_and_wait_for_result(transaction: Transaction, proxy: INetworkProvider, timeout: int) -> TransactionOnNetwork: if not transaction.signature: raise errors.TransactionIsNotSigned() diff --git a/multiversx_sdk_cli/validators/validators_file.py b/multiversx_sdk_cli/validators/validators_file.py index 2543180a..c0060f44 100644 --- a/multiversx_sdk_cli/validators/validators_file.py +++ b/multiversx_sdk_cli/validators/validators_file.py @@ -1,6 +1,5 @@ import json from pathlib import Path -from typing import Dict, List from multiversx_sdk import ValidatorPEM, ValidatorPublicKey, ValidatorSigner @@ -19,8 +18,8 @@ def get_num_of_nodes(self) -> int: def get_validators_list(self): return self._validators_data.get("validators", []) - def load_signers(self) -> List[ValidatorSigner]: - signers: List[ValidatorSigner] = [] + def load_signers(self) -> list[ValidatorSigner]: + signers: list[ValidatorSigner] = [] for validator in self.get_validators_list(): pem_file = self._load_validator_pem(validator) validator_signer = ValidatorSigner(pem_file.secret_key) @@ -28,8 +27,8 @@ def load_signers(self) -> List[ValidatorSigner]: return signers - def load_public_keys(self) -> List[ValidatorPublicKey]: - public_keys: List[ValidatorPublicKey] = [] + def load_public_keys(self) -> list[ValidatorPublicKey]: + public_keys: list[ValidatorPublicKey] = [] for validator in self.get_validators_list(): pem_file = self._load_validator_pem(validator) @@ -37,7 +36,7 @@ def load_public_keys(self) -> List[ValidatorPublicKey]: return public_keys - def _load_validator_pem(self, validator: Dict[str, str]) -> ValidatorPEM: + def _load_validator_pem(self, validator: dict[str, str]) -> ValidatorPEM: # Get path of "pemFile", make it absolute validator_pem = Path(validator.get("pemFile", "")).expanduser() validator_pem = ( From 9492d3b7ff3c86642b1cfb921cec269b096abe88 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 7 Feb 2025 13:46:42 +0200 Subject: [PATCH 29/84] fix typo --- multiversx_sdk_cli/cli_transactions.py | 2 +- multiversx_sdk_cli/transactions.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 8c0608ad..ebbcc8d7 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -151,7 +151,7 @@ def create_transaction(args: Any): sender=sender, receiver=Address.new_from_bech32(args.receiver), native_amount=native_amount, - gas_limt=args.gas_limit, + gas_limit=args.gas_limit, gas_price=args.gas_price, nonce=nonce, version=args.version, diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 1cc478f2..a5a2374a 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -48,7 +48,7 @@ def create_transaction( sender: IAccount, receiver: Address, native_amount: int, - gas_limt: int, + gas_limit: int, gas_price: int, nonce: int, version: int, @@ -67,7 +67,7 @@ def create_transaction( transaction = Transaction( sender=sender.address, receiver=receiver, - gas_limit=gas_limt, + gas_limit=gas_limit, chain_id=self.factory.config.chain_id, ) else: @@ -79,7 +79,7 @@ def create_transaction( data=data.encode() if data else None, ) - transaction.gas_limit = gas_limt + transaction.gas_limit = gas_limit transaction.gas_price = gas_price transaction.nonce = nonce transaction.version = version From 7e20cf9c2122146d3e37d8591ca3fbd0f35b8ded Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 7 Feb 2025 15:14:12 +0200 Subject: [PATCH 30/84] add tests for transactions controller --- multiversx_sdk_cli/tests/test_transactions.py | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 multiversx_sdk_cli/tests/test_transactions.py diff --git a/multiversx_sdk_cli/tests/test_transactions.py b/multiversx_sdk_cli/tests/test_transactions.py new file mode 100644 index 00000000..b5420b53 --- /dev/null +++ b/multiversx_sdk_cli/tests/test_transactions.py @@ -0,0 +1,237 @@ +from pathlib import Path + +from multiversx_sdk import Account, Address + +from multiversx_sdk_cli.transactions import TransactionsController + +testdata = Path(__file__).parent / "testdata" + + +class TestTransactionsController: + controller = TransactionsController("D") + alice = Account.new_from_pem(testdata / "alice.pem") + + def test_create_transaction_without_data_and_value(self): + transaction = self.controller.create_transaction( + sender=self.alice, + receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), + native_amount=0, + gas_limit=50000, + gas_price=1000000000, + nonce=7, + version=2, + options=0, + ) + + assert transaction.sender == self.alice.address + assert transaction.receiver.to_bech32() == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert transaction.value == 0 + assert transaction.chain_id == "D" + assert transaction.gas_limit == 50000 + assert transaction.gas_price == 1000000000 + assert transaction.nonce == 7 + assert transaction.version == 2 + assert transaction.options == 0 + assert not transaction.data + assert not transaction.guardian + assert not transaction.relayer + assert not transaction.guardian_signature + assert not transaction.relayer_signature + assert ( + transaction.signature.hex() + == "ecf9e9f8d395741c0fbb61eaba74295448ba380dd00a228463e29a278d4e3c9219b2fa54133e32510837e03d7cc9f17738b99b701e148ce098b8340064a5f409" + ) + + def test_create_transfer_transaction(self): + transaction = self.controller.create_transaction( + sender=self.alice, + receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), + native_amount=123456789, + gas_limit=50000, + gas_price=1000000000, + nonce=7, + version=2, + options=0, + ) + + assert transaction.sender == self.alice.address + assert transaction.receiver.to_bech32() == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert transaction.value == 123456789 + assert transaction.chain_id == "D" + assert transaction.gas_limit == 50000 + assert transaction.gas_price == 1000000000 + assert transaction.nonce == 7 + assert transaction.version == 2 + assert transaction.options == 0 + assert not transaction.data + assert not transaction.guardian + assert not transaction.relayer + assert not transaction.guardian_signature + assert not transaction.relayer_signature + assert ( + transaction.signature.hex() + == "fe0ff59c06c453eed882db562f98c0684a32763a79580a33e269af6edbebc150007118b298f34d17c54b90d55cee76ed1e5254832db8acb2c34ef12f14b8b40d" + ) + + def test_create_transaction_with_data(self): + transaction = self.controller.create_transaction( + sender=self.alice, + receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), + native_amount=0, + gas_limit=50000, + gas_price=1000000000, + nonce=7, + version=2, + options=0, + data="testdata", + ) + + assert transaction.sender == self.alice.address + assert transaction.receiver.to_bech32() == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert transaction.value == 0 + assert transaction.chain_id == "D" + assert transaction.gas_limit == 50000 + assert transaction.gas_price == 1000000000 + assert transaction.nonce == 7 + assert transaction.version == 2 + assert transaction.options == 0 + assert transaction.data == b"testdata" + assert not transaction.guardian + assert not transaction.relayer + assert not transaction.guardian_signature + assert not transaction.relayer_signature + assert ( + transaction.signature.hex() + == "74bb3bd0c4e87ed64a01456f888236a74b5672cf194a33a2225d868f5f43d65e9149f3145b9431075828ff842f70b977b778895925f37037db979e71563c540c" + ) + + def test_create_guarded_transaction(self): + transaction = self.controller.create_transaction( + sender=self.alice, + receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), + native_amount=0, + gas_limit=200000, + gas_price=1000000000, + nonce=7, + version=2, + options=0, + data="testdata", + guardian_address=Address.new_from_bech32("erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4"), + guardian_account=Account.new_from_pem(testdata / "testUser2.pem"), + ) + + assert transaction.sender == self.alice.address + assert transaction.receiver.to_bech32() == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert transaction.value == 0 + assert transaction.chain_id == "D" + assert transaction.gas_limit == 200000 + assert transaction.gas_price == 1000000000 + assert transaction.nonce == 7 + assert transaction.version == 2 + assert transaction.options == 2 + assert transaction.data == b"testdata" + assert not transaction.relayer + assert not transaction.relayer_signature + assert ( + transaction.guardian + and transaction.guardian.to_bech32() == "erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" + ) + assert ( + transaction.guardian_signature.hex() + == "c8b186f85c6e79e157aac48ec55d3f915abbc396e257d30aff193135485395c4b026e7921853366a16bcbbb8dbe5f6cf98a917a8ca11ccdc3f29f5a5a7d7af0c" + ) + assert ( + transaction.signature.hex() + == "8e9338de8f1d66bc6cd2f8e42cb62b5456d82ba66fdf9b8ace750ab3fa697f348084b41996b19d172b627b0b0a312ef76d29191d627b4d9959a3bf17409f780b" + ) + + def test_create_relayed_transaction(self): + transaction = self.controller.create_transaction( + sender=self.alice, + receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), + native_amount=0, + gas_limit=200000, + gas_price=1000000000, + nonce=7, + version=2, + options=0, + data="testdata", + relayer_address=Address.new_from_bech32("erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4"), + relayer_account=Account.new_from_pem(testdata / "testUser2.pem"), + ) + + assert transaction.sender == self.alice.address + assert transaction.receiver.to_bech32() == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert transaction.value == 0 + assert transaction.chain_id == "D" + assert transaction.gas_limit == 200000 + assert transaction.gas_price == 1000000000 + assert transaction.nonce == 7 + assert transaction.version == 2 + assert transaction.options == 0 + assert transaction.data == b"testdata" + assert not transaction.guardian + assert not transaction.guardian_signature + assert ( + transaction.relayer + and transaction.relayer.to_bech32() == "erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" + ) + assert ( + transaction.relayer_signature.hex() + == "385fb15816d52118b97a20451c2b225a81ce9be130ad13987453cab36e858f79af0473effc845bef1537ad9f878001e6fcdfeefa36c46c5e8bb6aab83c9b2a0b" + ) + assert ( + transaction.signature.hex() + == "89c2de2939bdd4ed2bdaf8f859f9020b6f7510b3a7298daaebdef53ce1de588181e1c27f8933745927e610ebfe6c41a8875e2b052a48fa22464903f3821b830e" + ) + + def test_create_guarded_relayed_transaction(self): + transaction = self.controller.create_transaction( + sender=self.alice, + receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), + native_amount=0, + gas_limit=200000, + gas_price=1000000000, + nonce=7, + version=2, + options=0, + data="testdata", + guardian_address=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), + guardian_account=Account.new_from_pem(testdata / "testUser.pem"), + relayer_address=Address.new_from_bech32("erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4"), + relayer_account=Account.new_from_pem(testdata / "testUser2.pem"), + ) + + assert transaction.sender == self.alice.address + assert transaction.receiver.to_bech32() == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert transaction.value == 0 + assert transaction.chain_id == "D" + assert transaction.gas_limit == 200000 + assert transaction.gas_price == 1000000000 + assert transaction.nonce == 7 + assert transaction.version == 2 + assert transaction.options == 2 + assert transaction.data == b"testdata" + + assert ( + transaction.guardian + and transaction.guardian.to_bech32() == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + ) + assert ( + transaction.guardian_signature.hex() + == "06bc457b510cae975e1cc6ab863ed765aadbd948ad23cbd204d66a2b92bee683922d308148af47e28d186bceba13474c79f16b26803c65c908f1d4a2e2ac6a0e" + ) + + assert ( + transaction.relayer + and transaction.relayer.to_bech32() == "erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" + ) + assert ( + transaction.relayer_signature.hex() + == "86e2460a6045ac3142c23f06c3b31fc132f38227faa25ffc8f7e9ce32c4542f6ed37d740d3c83c4454f2390befd223b968d4767c28991f574689b36faa978209" + ) + + assert ( + transaction.signature.hex() + == "b0bce606f2cfd52d6641f8e96aa29dcfd334af4707d88f81dba01dac67e72a99d7143dccadc8d120b387c4eaac84dc01865e57488a2162480a0f15acf50b6306" + ) From 762d5eccff210713168c624d3832ff7378c21f81 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 7 Feb 2025 17:45:02 +0200 Subject: [PATCH 31/84] fix mypy errors --- multiversx_sdk_cli/cli_deps.py | 20 +++--- multiversx_sdk_cli/cli_shared.py | 22 +++--- multiversx_sdk_cli/dns.py | 68 ++++++------------- multiversx_sdk_cli/localnet/config_root.py | 4 ++ .../localnet/config_software.py | 4 +- multiversx_sdk_cli/native_auth_client.py | 10 +-- .../tests/test_cli_contracts.py | 5 +- .../tests/test_cli_staking_provider.py | 3 +- .../tests/test_cli_transactions.py | 3 +- multiversx_sdk_cli/tests/test_cli_wallet.py | 7 +- multiversx_sdk_cli/tests/test_shared.py | 2 +- multiversx_sdk_cli/tests/test_testnet.py | 2 +- multiversx_sdk_cli/transactions.py | 22 +++--- 13 files changed, 71 insertions(+), 101 deletions(-) diff --git a/multiversx_sdk_cli/cli_deps.py b/multiversx_sdk_cli/cli_deps.py index 4c3c31ad..b73fd02e 100644 --- a/multiversx_sdk_cli/cli_deps.py +++ b/multiversx_sdk_cli/cli_deps.py @@ -57,16 +57,16 @@ def check(args: Any): if len(missing_dependencies): raise errors.DependenciesMissing(missing_dependencies) return - else: - module = dependencies.get_module_by_key(name) - tag_to_check: str = config.get_dependency_tag(module.key) - - is_installed = check_module_is_installed(module, tag_to_check) - if is_installed and name != "rust": - logger.info(f"[{module.key} {tag_to_check}] is installed.") - return - elif not is_installed: - raise errors.DependencyMissing(module.key, tag_to_check) + + module = dependencies.get_module_by_key(name) + tag_to_check = config.get_dependency_tag(module.key) + + is_installed = check_module_is_installed(module, tag_to_check) + if is_installed: + logger.info(f"[{module.key} {tag_to_check}] is installed.") + return + elif not is_installed: + raise errors.DependencyMissing(module.key, tag_to_check) def check_module_is_installed(module: DependencyModule, tag_to_check: str) -> bool: diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index f75078a7..ef67b14a 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -326,30 +326,28 @@ def prepare_account(args: Any): return Account.new_from_pem(file_path=Path(args.pem), index=args.pem_index, hrp=hrp) elif args.keyfile: password = load_password(args) - account = Account.new_from_keystore( + return Account.new_from_keystore( file_path=Path(args.keyfile), password=password, address_index=args.address_index, hrp=hrp, ) elif args.ledger: - account = LedgerAccount(address_index=args.ledger_address_index) + return LedgerAccount(address_index=args.ledger_address_index) else: raise errors.NoWalletProvided() - return account - def prepare_relayer_account(args: Any) -> IAccount: hrp = config.get_address_hrp() if args.relayer_ledger: - account = LedgerAccount(address_index=args.relayer_ledger_address_index) + return LedgerAccount(address_index=args.relayer_ledger_address_index) if args.relayer_pem: - account = Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_pem_index, hrp=hrp) + return Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_pem_index, hrp=hrp) elif args.relayer_keyfile: password = load_password(args) - account = Account.new_from_keystore( + return Account.new_from_keystore( file_path=Path(args.relayer_keyfile), password=password, address_index=args.relayer_address_index, @@ -358,29 +356,25 @@ def prepare_relayer_account(args: Any) -> IAccount: else: raise errors.NoWalletProvided() - return account - def prepare_guardian_account(args: Any) -> IAccount: hrp = config.get_address_hrp() if args.guardian_pem: - account = Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_pem_index, hrp=hrp) + return Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_pem_index, hrp=hrp) elif args.guardian_keyfile: password = load_guardian_password(args) - account = Account.new_from_keystore( + return Account.new_from_keystore( file_path=Path(args.guardian_keyfile), password=password, address_index=args.guardian_address_index, hrp=hrp, ) elif args.guardian_ledger: - account = LedgerAccount(address_index=args.relayer_ledger_address_index) + return LedgerAccount(address_index=args.relayer_ledger_address_index) else: raise errors.NoWalletProvided() - return account - def load_guardian_account(args: Any) -> Union[IAccount, None]: hrp = config.get_address_hrp() diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 3bacd1b4..a1498c99 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -1,19 +1,16 @@ -from typing import Any, List, Optional, Protocol, Union +from typing import Any, List, Protocol from Cryptodome.Hash import keccak from multiversx_sdk import ( Address, AddressComputer, - AwaitingOptions, - NetworkConfig, - TransactionOnNetwork, - TransactionsFactoryConfig, + SmartContractQuery, + SmartContractQueryResponse, ) from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX -from multiversx_sdk_cli.contracts import SmartContract from multiversx_sdk_cli.transactions import do_prepare_transaction MaxNumShards = 256 @@ -23,53 +20,40 @@ # fmt: off class INetworkProvider(Protocol): - def query_contract(self, query: Any) -> Any: - ... - - def get_network_config(self) -> NetworkConfig: - ... - - def await_transaction_completed( - self, transaction_hash: Union[bytes, str], options: Optional[AwaitingOptions] = None - ) -> TransactionOnNetwork: + def query_contract(self, query: SmartContractQuery) -> SmartContractQueryResponse: ... # fmt: on def resolve(name: str, proxy: INetworkProvider) -> Address: - name_arg = "0x{}".format(str.encode(name).hex()) dns_address = dns_address_for_name(name) - response = _query_contract(contract_address=dns_address, proxy=proxy, function="resolve", args=[name_arg]) + response = _query_contract(contract_address=dns_address, proxy=proxy, function="resolve", args=[name.encode()]) + print(response.__dict__) - if len(response) == 0: + if len(response.return_data_parts) == 0: return Address.new_from_hex(ADDRESS_ZERO_HEX, get_address_hrp()) - result = response[0].get("returnDataParts")[0] - return Address.new_from_hex(result, get_address_hrp()) + result = response.return_data_parts[0] + return Address(result, get_address_hrp()) def validate_name(name: str, shard_id: int, proxy: INetworkProvider): - name_arg = "0x{}".format(str.encode(name).hex()) dns_address = compute_dns_address_for_shard_id(shard_id) response = _query_contract( contract_address=dns_address, proxy=proxy, function="validateName", - args=[name_arg], + args=[name.encode()], ) - response = response[0] - - return_code = response["returnCode"] + return_code: str = response.return_code if return_code == "ok": print(f"name [{name}] is valid") else: print(f"name [{name}] is invalid") - print(response) - def register(args: Any): args = utils.as_object(args) @@ -107,22 +91,18 @@ def registration_cost(shard_id: int, proxy: INetworkProvider) -> int: args=[], ) - response = response[0] - - data = response["returnDataParts"][0] + data = response.return_data_parts[0] if not data: return 0 else: - return int("0x{}".format(data)) + return int.from_bytes(data, byteorder="big", signed=False) def version(shard_id: int, proxy: INetworkProvider) -> str: dns_address = compute_dns_address_for_shard_id(shard_id) response = _query_contract(contract_address=dns_address, proxy=proxy, function="version", args=[]) - - response = response[0] - return bytearray.fromhex(response["returnDataParts"][0]).decode() + return response.return_data_parts[0].decode() def dns_address_for_name(name: str) -> Address: @@ -147,15 +127,11 @@ def dns_register_data(name: str) -> str: return "register@{}".format(name_enc.hex()) -def _query_contract(contract_address: Address, proxy: INetworkProvider, function: str, args: List[Any]) -> List[Any]: - chain_id = proxy.get_network_config().chain_id - config = TransactionsFactoryConfig(chain_id) - contract = SmartContract(config) - - return contract.query_contract( - contract_address=contract_address, - proxy=proxy, - function=function, - arguments=args, - should_prepare_args=False, - ) +def _query_contract( + contract_address: Address, + proxy: INetworkProvider, + function: str, + args: List[bytes], +) -> SmartContractQueryResponse: + query = SmartContractQuery(contract=contract_address, function=function, arguments=args) + return proxy.query_contract(query) diff --git a/multiversx_sdk_cli/localnet/config_root.py b/multiversx_sdk_cli/localnet/config_root.py index af0a42b0..f6476462 100644 --- a/multiversx_sdk_cli/localnet/config_root.py +++ b/multiversx_sdk_cli/localnet/config_root.py @@ -7,6 +7,7 @@ from multiversx_sdk_cli.localnet import config_default from multiversx_sdk_cli.localnet.config_part import ConfigPart +from multiversx_sdk_cli.localnet.config_sharding import Metashard, RegularShards from multiversx_sdk_cli.localnet.constants import METACHAIN_ID from multiversx_sdk_cli.localnet.node import Node @@ -14,6 +15,9 @@ class ConfigRoot(ConfigPart): + shards: RegularShards + metashard: Metashard + def __init__(self): self.general = config_default.general self.software = config_default.software diff --git a/multiversx_sdk_cli/localnet/config_software.py b/multiversx_sdk_cli/localnet/config_software.py index 14e5b907..060069d5 100644 --- a/multiversx_sdk_cli/localnet/config_software.py +++ b/multiversx_sdk_cli/localnet/config_software.py @@ -66,10 +66,10 @@ def _verify(self): f"In configuration section '{self.get_name()}', resolution is '{self.resolution.value}', but 'local_path' is not a directory: {self.local_path}" ) - def get_archive_download_folder(self): + def get_archive_download_folder(self) -> Path: return self.archive_download_folder.expanduser().resolve() - def get_archive_extraction_folder(self): + def get_archive_extraction_folder(self) -> Path: return self.archive_extraction_folder.expanduser().resolve() def get_local_path(self): diff --git a/multiversx_sdk_cli/native_auth_client.py b/multiversx_sdk_cli/native_auth_client.py index 8979057a..639c97d5 100644 --- a/multiversx_sdk_cli/native_auth_client.py +++ b/multiversx_sdk_cli/native_auth_client.py @@ -54,8 +54,8 @@ def _get_current_block_hash_using_gateway(self) -> str: round = self._get_current_round() url = f"{self.config.gateway_url}/blocks/by-round/{round}" response = self._execute_request(url) - blocks = response["data"]["blocks"] - block = [b for b in blocks if b["shard"] == self.config.block_hash_shard][0] + blocks: list[dict[str, Any]] = response["data"]["blocks"] + block: dict[str, str] = [b for b in blocks if b["shard"] == self.config.block_hash_shard][0] return block["hash"] def _get_current_round(self) -> int: @@ -67,14 +67,14 @@ def _get_current_round(self) -> int: url = f"{self.config.gateway_url}/network/status/{self.config.block_hash_shard}" response = self._execute_request(url) - status = response["data"]["status"] + status: dict[str, int] = response["data"]["status"] return status["erd_current_round"] def _get_current_block_hash_using_api(self) -> str: try: url = f"{self.config.api_url}/blocks/latest?ttl={self.config.expiry_seconds}&fields=hash" - response = self._execute_request(url) + response: dict[str, str] = self._execute_request(url) if response["hash"]: return response["hash"] except Exception: @@ -88,7 +88,7 @@ def _get_current_block_hash_using_api_fallback(self) -> str: if self.config.block_hash_shard: url += f"&shard={self.config.block_hash_shard}" - response = self._execute_request(url) + response: list[dict[str, str]] = self._execute_request(url) return response[0]["hash"] def _encode_value(self, string: str) -> str: diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index 3679e062..bb822e70 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -617,7 +617,8 @@ def test_contract_query(capsys: Any): def _read_stdout(capsys: Any) -> str: - return capsys.readouterr().out.strip() + stdout: str = capsys.readouterr().out.strip() + return stdout def get_contract_address(capsys: Any): @@ -633,5 +634,5 @@ def get_query_response(capsys: Any): def get_transaction_data(capsys: Any) -> str: out = _read_stdout(capsys) - output = json.loads(out) + output: dict[str, str] = json.loads(out) return output["emittedTransactionData"] diff --git a/multiversx_sdk_cli/tests/test_cli_staking_provider.py b/multiversx_sdk_cli/tests/test_cli_staking_provider.py index 82e21c2b..2e0153eb 100644 --- a/multiversx_sdk_cli/tests/test_cli_staking_provider.py +++ b/multiversx_sdk_cli/tests/test_cli_staking_provider.py @@ -742,7 +742,8 @@ def test_withdraw(capsys: Any): def _read_stdout(capsys: Any) -> str: - return capsys.readouterr().out.strip() + stdout: str = capsys.readouterr().out.strip() + return stdout def get_transaction(capsys: Any): diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index 3dd83bcb..37287428 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -329,4 +329,5 @@ def test_create_plain_transaction(capsys: Any): def _read_stdout(capsys: Any) -> str: - return capsys.readouterr().out.strip() + stdout: str = capsys.readouterr().out.strip() + return stdout diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index 67b910e5..50605851 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -334,9 +334,7 @@ def test_wallet_convert_pem_to_pubkey(capsys: Any): def test_wallet_convert_pem_to_secret_key(capsys: Any): infile = testdata_path / "alice.pem" - main([ - "wallet", "convert", "--infile", str(infile), "--in-format", "pem", "--out-format", "secret-key" - ]) + main(["wallet", "convert", "--infile", str(infile), "--in-format", "pem", "--out-format", "secret-key"]) out = _read_stdout(capsys).strip("Output:\n\n") assert out == "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9" @@ -538,7 +536,8 @@ def _read_stdout_wallet_address(capsys: Any) -> str: def _read_stdout(capsys: Any) -> str: - return capsys.readouterr().out.strip() + stdout: str = capsys.readouterr().out.strip() + return stdout def _mock_getpass(monkeypatch: Any, password: str): diff --git a/multiversx_sdk_cli/tests/test_shared.py b/multiversx_sdk_cli/tests/test_shared.py index caa43b3c..31208d61 100644 --- a/multiversx_sdk_cli/tests/test_shared.py +++ b/multiversx_sdk_cli/tests/test_shared.py @@ -10,7 +10,7 @@ class Args: pass -def test_prepare_chain_id_in_args(): +def test_prepare_chain_id_in_args() -> None: args: Any = Args() args.chain = None args.proxy = None diff --git a/multiversx_sdk_cli/tests/test_testnet.py b/multiversx_sdk_cli/tests/test_testnet.py index 4f29b83f..999219b5 100644 --- a/multiversx_sdk_cli/tests/test_testnet.py +++ b/multiversx_sdk_cli/tests/test_testnet.py @@ -8,7 +8,7 @@ sys.path = [os.getcwd() + "/.."] + sys.path -def test_override_config(): +def test_override_config() -> None: config = ConfigRoot() # Check a few default values diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index a5a2374a..8af6bb14 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -182,30 +182,28 @@ def load_sender_account_from_args(args: Any) -> IAccount: return Account.new_from_pem(file_path=Path(args.pem), index=args.pem_index, hrp=hrp) elif args.keyfile: password = load_password(args) - account = Account.new_from_keystore( + return Account.new_from_keystore( file_path=Path(args.keyfile), password=password, address_index=args.address_index, hrp=hrp, ) elif args.ledger: - account = LedgerAccount(address_index=args.ledger_address_index) + return LedgerAccount(address_index=args.ledger_address_index) else: raise errors.NoWalletProvided() - return account - def load_relayer_account_from_args(args: Any) -> IAccount: hrp = config.get_address_hrp() if args.relayer_ledger: - account = LedgerAccount(address_index=args.relayer_ledger_address_index) + return LedgerAccount(address_index=args.relayer_ledger_address_index) if args.relayer_pem: - account = Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_pem_index, hrp=hrp) + return Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_pem_index, hrp=hrp) elif args.relayer_keyfile: password = load_password(args) - account = Account.new_from_keystore( + return Account.new_from_keystore( file_path=Path(args.relayer_keyfile), password=password, address_index=args.relayer_address_index, @@ -214,8 +212,6 @@ def load_relayer_account_from_args(args: Any) -> IAccount: else: raise errors.NoWalletProvided() - return account - def prepare_token_transfers(transfers: List[Any]) -> List[TokenTransfer]: token_computer = TokenComputer() @@ -247,22 +243,20 @@ def get_guardian_account_from_args(args: Any) -> IAccount: hrp = config.get_address_hrp() if args.guardian_pem: - account = Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_pem_index, hrp=hrp) + return Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_pem_index, hrp=hrp) elif args.guardian_keyfile: password = load_guardian_password(args) - account = Account.new_from_keystore( + return Account.new_from_keystore( file_path=Path(args.guardian_keyfile), password=password, address_index=args.guardian_address_index, hrp=hrp, ) elif args.guardian_ledger: - account = LedgerAccount(address_index=args.relayer_ledger_address_index) + return LedgerAccount(address_index=args.relayer_ledger_address_index) else: raise errors.NoWalletProvided() - return account - def send_and_wait_for_result(transaction: Transaction, proxy: INetworkProvider, timeout: int) -> TransactionOnNetwork: if not transaction.signature: From a6e6d104fbd5a313de17f685b92d0ed6c5b8d6c2 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 10 Feb 2025 10:45:04 +0200 Subject: [PATCH 32/84] fixes after review --- .../base_transactions_controller.py | 3 ++- multiversx_sdk_cli/cli_shared.py | 20 +++++++++---------- multiversx_sdk_cli/cli_transactions.py | 2 +- .../tests/test_cli_transactions.py | 6 +++--- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/multiversx_sdk_cli/base_transactions_controller.py b/multiversx_sdk_cli/base_transactions_controller.py index e5e177f0..bc964561 100644 --- a/multiversx_sdk_cli/base_transactions_controller.py +++ b/multiversx_sdk_cli/base_transactions_controller.py @@ -19,7 +19,7 @@ def sign_transaction( guardian_service_url: str = "", guardian_2fa_code: str = "", ): - """Signs the transaction with the sender's account and if necessarry, signs with guardian's account and relayer's account. Also sets proper transaction options if needed.""" + """Signs the transaction using the sender's account and, if required, additionally signs with the guardian's and relayer's accounts. Ensures the appropriate transaction options are set as needed.""" self._set_options_for_guarded_transaction_if_needed(transaction) self._set_options_for_hash_signing_if_needed(transaction, sender, guardian, relayer) @@ -60,6 +60,7 @@ def _sign_guarded_transaction_if_guardian( guardian_service_url: str, guardian_2fa_code: str, ) -> Transaction: + # If the guardian account is provided, we sign locally. Otherwise, we reach for the trusted cosign service. if guardian: transaction.guardian_signature = guardian.sign_transaction(transaction) elif transaction.guardian and guardian_service_url and guardian_2fa_code: diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index f75078a7..f58d809f 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -402,23 +402,23 @@ def load_guardian_account(args: Any) -> Union[IAccount, None]: def get_guardian_address(guardian: Union[IAccount, None], args: Any) -> Union[Address, None]: - address_pem = guardian.address if guardian else None - address_arg = Address.new_from_bech32(args.guardian) if hasattr(args, "guardian") and args.guardian else None + address_from_account = guardian.address if guardian else None + address_from_args = Address.new_from_bech32(args.guardian) if hasattr(args, "guardian") and args.guardian else None - if address_pem and address_arg and address_pem != address_arg: - raise IncorrectWalletError("Guardian wallet does not match the guardian's address set in the transaction.") + if address_from_account and address_from_args and address_from_account != address_from_args: + raise IncorrectWalletError("Guardian wallet does not match the guardian's address set in the arguments.") - return address_pem or address_arg + return address_from_account or address_from_args def get_relayer_address(relayer: Union[IAccount, None], args: Any) -> Union[Address, None]: - address_pem = relayer.address if relayer else None - address_arg = Address.new_from_bech32(args.relayer) if hasattr(args, "relayer") and args.relayer else None + address_from_account = relayer.address if relayer else None + address_from_args = Address.new_from_bech32(args.relayer) if hasattr(args, "relayer") and args.relayer else None - if address_pem and address_arg and address_pem != address_arg: - raise IncorrectWalletError("Relayer wallet does not match the relayer's address set in the transaction.") + if address_from_account and address_from_args and address_from_account != address_from_args: + raise IncorrectWalletError("Relayer wallet does not match the relayer's address set in the arguments.") - return address_pem or address_arg + return address_from_account or address_from_args def load_relayer_account(args: Any) -> Union[IAccount, None]: diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index ebbcc8d7..7d6ab74c 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -201,7 +201,6 @@ def send_transaction(args: Any): def get_transaction(args: Any): - # args = utils.as_object(args) omit_fields = cli_shared.parse_omit_fields_arg(args) config = get_config_for_network_providers() @@ -231,6 +230,7 @@ def sign_transaction(args: Any): if guardian_account: tx.guardian_signature = guardian_account.sign_transaction(tx) elif args.guardian: + # If the guardian account is provided, we sign locally. Otherwise, we reach for the trusted cosign service. cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index 3dd83bcb..aa8ae58f 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -308,7 +308,7 @@ def test_create_plain_transaction(capsys: Any): "--gas-limit", "50000", "--chain", - "integration tests chain ID", + "test", ] ) assert return_code == 0 @@ -318,13 +318,13 @@ def test_create_plain_transaction(capsys: Any): assert tx_json["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert tx_json["receiver"] == "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" - assert tx_json["chainID"] == "integration tests chain ID" + assert tx_json["chainID"] == "test" assert tx_json["gasLimit"] == 50000 assert tx_json["version"] == 2 assert tx_json["options"] == 0 assert ( tx_json["signature"] - == "210d3e75924858cde8c7f3020b38db32890a79101cadcb32dc04980e4f3a378a14e9517ff805881f6444efc61fb38b7dfcf3fee07d4b87fa254ee96b67681e02" + == "0cbb3cb4d6feaf9d2e6d17a529ddb5eeb0fd547af1dde65362beb6aaf54b78d90d429fa951b6ce7b52724be8da9737d7efaf13631816d034a2d7d1f5ae19510b" ) From 6df2e3ed029ada37b8d04ef037df506a2637d625 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 10 Feb 2025 14:09:04 +0200 Subject: [PATCH 33/84] fix flow of signing stored transactions --- .../base_transactions_controller.py | 7 +- multiversx_sdk_cli/cli_contracts.py | 6 +- multiversx_sdk_cli/cli_delegation.py | 2 +- multiversx_sdk_cli/cli_shared.py | 56 ++++++++-------- multiversx_sdk_cli/cli_transactions.py | 50 ++++++++------ multiversx_sdk_cli/dns.py | 2 +- .../tests/test_cli_transactions.py | 65 +++++++++++++++++++ 7 files changed, 133 insertions(+), 55 deletions(-) diff --git a/multiversx_sdk_cli/base_transactions_controller.py b/multiversx_sdk_cli/base_transactions_controller.py index bc964561..596e26b4 100644 --- a/multiversx_sdk_cli/base_transactions_controller.py +++ b/multiversx_sdk_cli/base_transactions_controller.py @@ -13,7 +13,7 @@ def __init__(self) -> None: def sign_transaction( self, transaction: Transaction, - sender: IAccount, + sender: Optional[IAccount] = None, guardian: Optional[IAccount] = None, relayer: Optional[IAccount] = None, guardian_service_url: str = "", @@ -23,7 +23,8 @@ def sign_transaction( self._set_options_for_guarded_transaction_if_needed(transaction) self._set_options_for_hash_signing_if_needed(transaction, sender, guardian, relayer) - transaction.signature = sender.sign_transaction(transaction) + if sender: + transaction.signature = sender.sign_transaction(transaction) self._sign_guarded_transaction_if_guardian( transaction, @@ -41,7 +42,7 @@ def _set_options_for_guarded_transaction_if_needed(self, transaction: Transactio def _set_options_for_hash_signing_if_needed( self, transaction: Transaction, - sender: IAccount, + sender: Union[IAccount, None], guardian: Union[IAccount, None], relayer: Union[IAccount, None], ): diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index bd47c4dd..f141cd58 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -336,7 +336,7 @@ def build(args: Any): def deploy(args: Any): logger.debug("deploy") - cli_shared.check_guardian_and_options_args(args) + cli_shared.check_guardian_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_chain_id_in_args(args) @@ -392,7 +392,7 @@ def deploy(args: Any): def call(args: Any): logger.debug("call") - cli_shared.check_guardian_and_options_args(args) + cli_shared.check_guardian_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_chain_id_in_args(args) @@ -441,7 +441,7 @@ def call(args: Any): def upgrade(args: Any): logger.debug("upgrade") - cli_shared.check_guardian_and_options_args(args) + cli_shared.check_guardian_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_chain_id_in_args(args) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index c00c2869..0792aca9 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -363,7 +363,7 @@ def _add_common_arguments(args: List[str], sub: Any): def ensure_arguments_are_provided_and_prepared(args: Any): - cli_shared.check_guardian_and_options_args(args) + cli_shared.check_guardian_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_chain_id_in_args(args) cli_shared.prepare_nonce_in_args(args) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index f58d809f..88c8cc83 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -21,11 +21,7 @@ load_password, load_relayer_password, ) -from multiversx_sdk_cli.constants import ( - DEFAULT_GAS_PRICE, - DEFAULT_TX_VERSION, - TRANSACTION_OPTIONS_TX_GUARDED, -) +from multiversx_sdk_cli.constants import DEFAULT_GAS_PRICE, DEFAULT_TX_VERSION from multiversx_sdk_cli.errors import ArgumentsNotProvidedError, IncorrectWalletError from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.simulation import Simulator @@ -156,10 +152,10 @@ def add_guardian_args(sub: Any): ) -def add_wallet_args(args: List[str], sub: Any): +def add_wallet_args(args: List[str], sub: Any, skip_required_check: bool = False): sub.add_argument( "--pem", - required=check_if_sign_method_required(args, "--pem"), + required=check_if_sign_method_required(args, "--pem", skip_required_check), help="🔑 the PEM file, if keyfile not provided", ) sub.add_argument( @@ -170,7 +166,7 @@ def add_wallet_args(args: List[str], sub: Any): ) sub.add_argument( "--keyfile", - required=check_if_sign_method_required(args, "--keyfile"), + required=check_if_sign_method_required(args, "--keyfile", skip_required_check), help="🔑 a JSON keyfile, if PEM not provided", ) sub.add_argument( @@ -186,7 +182,7 @@ def add_wallet_args(args: List[str], sub: Any): sub.add_argument( "--ledger", action="store_true", - required=check_if_sign_method_required(args, "--ledger"), + required=check_if_sign_method_required(args, "--ledger", skip_required_check), default=False, help="🔐 bool flag for signing transaction using ledger", ) @@ -202,7 +198,6 @@ def add_wallet_args(args: List[str], sub: Any): def add_guardian_wallet_args(args: List[str], sub: Any): sub.add_argument( "--guardian-pem", - required=check_if_sign_method_required(args, "--guardian-pem"), help="🔑 the PEM file, if keyfile not provided", ) sub.add_argument( @@ -213,7 +208,6 @@ def add_guardian_wallet_args(args: List[str], sub: Any): ) sub.add_argument( "--guardian-keyfile", - required=check_if_sign_method_required(args, "--guardian-keyfile"), help="🔑 a JSON keyfile, if PEM not provided", ) sub.add_argument( @@ -229,7 +223,6 @@ def add_guardian_wallet_args(args: List[str], sub: Any): sub.add_argument( "--guardian-ledger", action="store_true", - required=check_if_sign_method_required(args, "--guardian-ledger"), default=False, help="🔐 bool flag for signing transaction using ledger", ) @@ -241,7 +234,6 @@ def add_guardian_wallet_args(args: List[str], sub: Any): ) -# Required check not properly working, same for guardian. Will be refactored in the future. def add_relayed_v3_wallet_args(args: List[str], sub: Any): sub.add_argument("--relayer-pem", help="🔑 the PEM file, if keyfile not provided") sub.add_argument( @@ -382,6 +374,25 @@ def prepare_guardian_account(args: Any) -> IAccount: return account +def load_sender_account(args: Any) -> Union[IAccount, None]: + hrp = config.get_address_hrp() + + if args.pem: + return Account.new_from_pem(file_path=Path(args.pem), index=args.pem_index, hrp=hrp) + elif args.keyfile: + password = load_password(args) + return Account.new_from_keystore( + file_path=Path(args.keyfile), + password=password, + address_index=args.address_index, + hrp=hrp, + ) + elif args.ledger: + return LedgerAccount(address_index=args.ledger_address_index) + + return None + + def load_guardian_account(args: Any) -> Union[IAccount, None]: hrp = config.get_address_hrp() @@ -508,20 +519,11 @@ def check_broadcast_args(args: Any): raise errors.BadUsage("Cannot both 'simulate' and 'send' a transaction") -def check_guardian_and_options_args(args: Any): - check_guardian_args(args) - if args.guardian: - check_options_for_guarded_tx(args.options) - - def check_guardian_args(args: Any): if args.guardian: if should_sign_with_cosigner_service(args) and should_sign_with_guardian_key(args): raise errors.BadUsage("Guarded tx should be signed using either a cosigning service or a guardian key") - if not should_sign_with_cosigner_service(args) and not should_sign_with_guardian_key(args): - raise errors.BadUsage("Missing guardian signing arguments") - def should_sign_with_cosigner_service(args: Any) -> bool: return all([args.guardian_service_url, args.guardian_2fa_code]) @@ -531,11 +533,6 @@ def should_sign_with_guardian_key(args: Any) -> bool: return any([args.guardian_pem, args.guardian_keyfile, args.guardian_ledger]) -def check_options_for_guarded_tx(options: int): - if not options & TRANSACTION_OPTIONS_TX_GUARDED == TRANSACTION_OPTIONS_TX_GUARDED: - raise errors.BadUsage("Invalid guarded transaction's options. The second least significant bit must be set") - - def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CLIOutputBuilder: network_provider_config = config.get_config_for_network_providers() proxy = ProxyNetworkProvider(url=args.proxy, config=network_provider_config) @@ -577,7 +574,10 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL return output_builder -def check_if_sign_method_required(args: List[str], checked_method: str) -> bool: +def check_if_sign_method_required(args: List[str], checked_method: str, skip_required_check: bool = False) -> bool: + if skip_required_check: + return False + methods = ["--pem", "--keyfile", "--ledger"] rest_of_methods: List[str] = [] for method in methods: diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 7d6ab74c..7ae0cb2e 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -8,13 +8,14 @@ Token, TokenComputer, TokenTransfer, + TransactionComputer, ) from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers -from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.errors import IncorrectWalletError, NoWalletProvided +from multiversx_sdk_cli.errors import BadUsage, IncorrectWalletError, NoWalletProvided from multiversx_sdk_cli.transactions import ( TransactionsController, load_transaction_from_file, @@ -88,7 +89,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "sign", f"Sign a previously saved transaction.{CLIOutputBuilder.describe()}", ) - cli_shared.add_wallet_args(args, sub) + # we add the wallet args, but don't make the args mandatory + cli_shared.add_wallet_args(args, sub, True) cli_shared.add_infile_arg(sub, what="a previously saved transaction") cli_shared.add_outfile_arg(sub, what="the signed transaction") cli_shared.add_broadcast_args(sub) @@ -122,7 +124,7 @@ def _add_common_arguments(args: list[str], sub: Any): def create_transaction(args: Any): - cli_shared.check_guardian_and_options_args(args) + cli_shared.check_guardian_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_chain_id_in_args(args) @@ -212,26 +214,36 @@ def get_transaction(args: Any): def sign_transaction(args: Any): - cli_shared.check_guardian_args(args) cli_shared.check_broadcast_args(args) tx = load_transaction_from_file(args.infile) - if args.guardian: - cli_shared.check_options_for_guarded_tx(tx.options) - account = cli_shared.prepare_account(args) - tx.signature = account.sign_transaction(tx) + sender = cli_shared.load_sender_account(args) + if sender and sender.address != tx.sender: + raise IncorrectWalletError("Sender's wallet does not match transaction's sender.") - try: - guardian_account = cli_shared.prepare_guardian_account(args) - except NoWalletProvided: - guardian_account = None - - if guardian_account: - tx.guardian_signature = guardian_account.sign_transaction(tx) - elif args.guardian: - # If the guardian account is provided, we sign locally. Otherwise, we reach for the trusted cosign service. - cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) + relayer = cli_shared.load_relayer_account(args) + if relayer and relayer.address != tx.relayer: + raise IncorrectWalletError("Relayer's wallet does not match transaction's relayer.") + + guardian = cli_shared.load_guardian_account(args) + if guardian: + if guardian.address != tx.guardian: + raise IncorrectWalletError("Guardian's wallet does not match transaction's guardian.") + + tx_computer = TransactionComputer() + if tx.guardian and not tx_computer.has_options_set_for_guarded_transaction(tx): + raise BadUsage("Guardian wallet provided but the transaction has incorrect options.") + + tx_controller = BaseTransactionsController() + tx_controller.sign_transaction( + transaction=tx, + sender=sender, + guardian=guardian, + relayer=relayer, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 3bacd1b4..a3bf48ec 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -74,7 +74,7 @@ def validate_name(name: str, shard_id: int, proxy: INetworkProvider): def register(args: Any): args = utils.as_object(args) - cli_shared.check_guardian_and_options_args(args) + cli_shared.check_guardian_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_nonce_in_args(args) cli_shared.prepare_chain_id_in_args(args) diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index aa8ae58f..a398dfc3 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -328,5 +328,70 @@ def test_create_plain_transaction(capsys: Any): ) +def test_sign_transaction(capsys: Any): + return_code = main( + [ + "tx", + "new", + "--pem", + str(testdata_path / "alice.pem"), + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", + "89", + "--gas-limit", + "50000", + "--chain", + "test", + "--outfile", + str(testdata_out / "transaction.json"), + "--guardian", + "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--relayer", + "erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4", + ] + ) + assert return_code == 0 + assert (testdata_out / "transaction.json").is_file() + + return_code = main( + [ + "tx", + "sign", + "--infile", + str(testdata_out / "transaction.json"), + "--guardian-pem", + str(testdata_path / "testUser.pem"), + "--relayer-pem", + str(testdata_path / "testUser2.pem"), + ] + ) + assert return_code == 0 + + tx = _read_stdout(capsys) + tx_json = json.loads(tx)["emittedTransaction"] + + assert tx_json["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx_json["receiver"] == "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" + assert tx_json["chainID"] == "test" + assert tx_json["gasLimit"] == 50000 + assert tx_json["version"] == 2 + assert tx_json["options"] == 2 + assert tx_json["guardian"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert tx_json["relayer"] == "erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" + assert ( + tx_json["signature"] + == "96cd2db86f8f63e8fd40a3eb9a9a5a7b0e33a8c5879fe2feb6eba19657ddaf966f9c1d67bbb20c93dcea833a29531426414afe6b716142c9e4e82e7b87454b05" + ) + assert ( + tx_json["guardianSignature"] + == "3197aaa08cc5589c782928c3d123f38c03c5ddcecb666ce72d6751655c08fe2eb684592a165bc214044e549541d2dd3f2f209e7fab158e0adbf47f0af322200b" + ) + assert ( + tx_json["relayerSignature"] + == "01357f907ad0dd83d37a916d39aa4922d831279075f25a039bd9ebfc050eb67e72faeac07463bc1bc2c5e84711dd1ca9d0d21283664ba92c2036a2f01eae2c0d" + ) + + def _read_stdout(capsys: Any) -> str: return capsys.readouterr().out.strip() From 8ef4ba8801f4327f9a598450fb20998187742f1b Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 10 Feb 2025 17:23:12 +0200 Subject: [PATCH 34/84] fix small comments from previous PR --- multiversx_sdk_cli/cli_shared.py | 10 ++++++++-- multiversx_sdk_cli/cli_transactions.py | 2 +- multiversx_sdk_cli/dns.py | 1 - 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 4d87a38c..aa5eaefa 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -410,7 +410,7 @@ def get_guardian_address(guardian: Union[IAccount, None], args: Any) -> Union[Ad address_from_account = guardian.address if guardian else None address_from_args = Address.new_from_bech32(args.guardian) if hasattr(args, "guardian") and args.guardian else None - if address_from_account and address_from_args and address_from_account != address_from_args: + if not _is_matching_address(address_from_account, address_from_args): raise IncorrectWalletError("Guardian wallet does not match the guardian's address set in the arguments.") return address_from_account or address_from_args @@ -420,12 +420,18 @@ def get_relayer_address(relayer: Union[IAccount, None], args: Any) -> Union[Addr address_from_account = relayer.address if relayer else None address_from_args = Address.new_from_bech32(args.relayer) if hasattr(args, "relayer") and args.relayer else None - if address_from_account and address_from_args and address_from_account != address_from_args: + if not _is_matching_address(address_from_account, address_from_args): raise IncorrectWalletError("Relayer wallet does not match the relayer's address set in the arguments.") return address_from_account or address_from_args +def _is_matching_address(account_address: Union[Address, None], args_address: Union[Address, None]) -> bool: + if account_address and args_address and account_address != args_address: + return False + return True + + def load_relayer_account(args: Any) -> Union[IAccount, None]: hrp = config.get_address_hrp() diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 7ae0cb2e..2bb54814 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -90,7 +90,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: f"Sign a previously saved transaction.{CLIOutputBuilder.describe()}", ) # we add the wallet args, but don't make the args mandatory - cli_shared.add_wallet_args(args, sub, True) + cli_shared.add_wallet_args(args=args, sub=sub, skip_required_check=True) cli_shared.add_infile_arg(sub, what="a previously saved transaction") cli_shared.add_outfile_arg(sub, what="the signed transaction") cli_shared.add_broadcast_args(sub) diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 930ff0a5..60a9d981 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -29,7 +29,6 @@ def resolve(name: str, proxy: INetworkProvider) -> Address: dns_address = dns_address_for_name(name) response = _query_contract(contract_address=dns_address, proxy=proxy, function="resolve", args=[name.encode()]) - print(response.__dict__) if len(response.return_data_parts) == 0: return Address.new_from_hex(ADDRESS_ZERO_HEX, get_address_hrp()) From 4062bd23bb5a6e5a91979001fed98d67680dd95e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 10 Feb 2025 17:32:04 +0200 Subject: [PATCH 35/84] move properties type definition inside constructor --- multiversx_sdk_cli/localnet/config_root.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/multiversx_sdk_cli/localnet/config_root.py b/multiversx_sdk_cli/localnet/config_root.py index f6476462..f4b3ea91 100644 --- a/multiversx_sdk_cli/localnet/config_root.py +++ b/multiversx_sdk_cli/localnet/config_root.py @@ -15,10 +15,10 @@ class ConfigRoot(ConfigPart): - shards: RegularShards - metashard: Metashard + def __init__(self) -> None: + self.shards: RegularShards + self.metashard: Metashard - def __init__(self): self.general = config_default.general self.software = config_default.software self.metashard = config_default.metashard From 622105f141202382f748207c697e19e33373feb8 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 10 Feb 2025 17:34:05 +0200 Subject: [PATCH 36/84] fix --- multiversx_sdk_cli/localnet/config_root.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/multiversx_sdk_cli/localnet/config_root.py b/multiversx_sdk_cli/localnet/config_root.py index f4b3ea91..a40f90a1 100644 --- a/multiversx_sdk_cli/localnet/config_root.py +++ b/multiversx_sdk_cli/localnet/config_root.py @@ -16,13 +16,10 @@ class ConfigRoot(ConfigPart): def __init__(self) -> None: - self.shards: RegularShards - self.metashard: Metashard - self.general = config_default.general self.software = config_default.software - self.metashard = config_default.metashard - self.shards = config_default.shards + self.metashard: Metashard = config_default.metashard + self.shards: RegularShards = config_default.shards self.networking = config_default.networking def get_name(self) -> str: From 55d2ee6a58fdaad9e31b9e2e99b031b9eb32e07a Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 11 Feb 2025 11:37:41 +0200 Subject: [PATCH 37/84] add support for creating validator wallet --- multiversx_sdk_cli/cli_wallet.py | 65 +++++++++++++++++---- multiversx_sdk_cli/tests/test_cli_wallet.py | 39 ++++++++++++- 2 files changed, 93 insertions(+), 11 deletions(-) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 9dff11bd..951a469b 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -3,15 +3,28 @@ import logging import sys from pathlib import Path -from typing import Any, List, Optional, Tuple - -from multiversx_sdk import Address, Mnemonic, UserPEM, UserSecretKey, UserWallet +from typing import Any, Optional, Union + +from multiversx_sdk import ( + Address, + Mnemonic, + UserPEM, + UserSecretKey, + UserWallet, + ValidatorPEM, + ValidatorSecretKey, +) from multiversx_sdk.core.address import get_shard_of_pubkey from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS -from multiversx_sdk_cli.errors import BadUserInput, KnownError, WalletGenerationError +from multiversx_sdk_cli.errors import ( + BadUsage, + BadUserInput, + KnownError, + WalletGenerationError, +) from multiversx_sdk_cli.sign_verify import SignedMessage, sign_message from multiversx_sdk_cli.ux import show_critical_error, show_message @@ -43,7 +56,7 @@ CURRENT_SHARDS = [i for i in range(NUMBER_OF_SHARDS)] -def setup_parser(args: List[str], subparsers: Any) -> Any: +def setup_parser(args: list[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser( subparsers, "wallet", @@ -57,6 +70,12 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: "new", "Create a new wallet and print its mnemonic; optionally save as password-protected JSON (recommended) or PEM (not recommended)", ) + sub.add_argument( + "--validator-wallet", + help="Create a validator wallet. If flag is provided, only the `pem` format is supported. Address hrp and shard will be ignored if provided.", + action="store_true", + default=False, + ) sub.add_argument( "--format", choices=WALLET_FORMATS, @@ -155,6 +174,11 @@ def wallet_new(args: Any): address_hrp = args.address_hrp shard = args.shard + if args.validator_wallet: + _ensure_correct_arguments_for_validator_wallet(format, outfile) + _generate_validator_wallet(outfile) + return + if shard is not None: mnemonic = _generate_mnemonic_with_shard_constraint(shard) else: @@ -166,11 +190,11 @@ def wallet_new(args: Any): if format is None: return if outfile is None: - raise KnownError("The --outfile option is required when --format is specified.") + raise BadUsage("The `--outfile` argument is required when `--format` is specified.") outfile = Path(outfile).expanduser().resolve() if outfile.exists(): - raise KnownError(f"File already exists, will not overwrite: {outfile}") + raise BadUserInput(f"File already exists, will not overwrite: {outfile}") if format == WALLET_FORMAT_RAW_MNEMONIC: outfile.write_text(mnemonic.get_text()) @@ -190,13 +214,34 @@ def wallet_new(args: Any): pem_file = UserPEM(address.to_bech32(), secret_key) pem_file.save(outfile) else: - raise KnownError(f"Unknown format: {format}") + raise BadUsage(f"Unknown format: {format}") logger.info(f"Wallet ({format}) saved: {outfile}") -def _generate_mnemonic_with_shard_constraint(shard: int) -> Mnemonic: +def _generate_validator_wallet(outfile: str): + path = Path(outfile).expanduser().resolve() + secret_key = ValidatorSecretKey.generate() + public_key = secret_key.generate_public_key() + validator_pem = ValidatorPEM(label=public_key.hex(), secret_key=secret_key) + validator_pem.save(path) + + logger.info(f"Validator wallet saved: {str(path)}") + +def _ensure_correct_arguments_for_validator_wallet(format: str, outfile: Union[str, None]): + if format != WALLET_FORMAT_PEM: + raise BadUsage("Only PEM format supported for validator wallet") + + if outfile is None: + raise BadUsage("The `--outfile` argument is required") + + path = Path(outfile).expanduser().resolve() + if path.exists(): + raise BadUserInput(f"File already exists, will not overwrite: {str(path)}") + + +def _generate_mnemonic_with_shard_constraint(shard: int) -> Mnemonic: if shard not in CURRENT_SHARDS: raise BadUserInput(f"Wrong shard provided. Choose between {CURRENT_SHARDS}") @@ -241,7 +286,7 @@ def convert_wallet(args: Any): def _load_wallet( input_text: str, in_format: str, address_index: int -) -> Tuple[Optional[Mnemonic], Optional[UserSecretKey]]: +) -> tuple[Optional[Mnemonic], Optional[UserSecretKey]]: if in_format == WALLET_FORMAT_RAW_MNEMONIC: input_text = " ".join(input_text.split()) mnemonic = Mnemonic(input_text) diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index 50605851..a11b7f9f 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -3,7 +3,14 @@ from pathlib import Path from typing import Any -from multiversx_sdk import Address, AddressComputer, Mnemonic, UserPEM, UserWallet +from multiversx_sdk import ( + Address, + AddressComputer, + Mnemonic, + UserPEM, + UserWallet, + ValidatorPEM, +) from multiversx_sdk_cli.cli import main @@ -126,6 +133,36 @@ def test_wallet_new_as_keystore_with_secret_key(capsys: Any, monkeypatch: Any): assert actual_secret_key.hex() == expected_secret_key.hex() +def test_create_validator_wallet(): + return_code = main(["wallet", "new", "--validator-wallet"]) + assert return_code + + return_code = main(["wallet", "new", "--validator-wallet", "--format", "pem"]) + assert return_code + + return_code = main(["wallet", "new", "--validator-wallet", "--outfile", str(testdata_out_path / "validator.pem")]) + assert return_code + + path = testdata_out_path / "validator.pem" + return_code = main( + [ + "wallet", + "new", + "--validator-wallet", + "--format", + "pem", + "--outfile", + str(path), + ] + ) + assert not return_code + assert path.is_file() + + wallet = ValidatorPEM.from_file(path) + assert wallet.label + assert wallet.secret_key + + def test_wallet_convert_raw_mnemonic_to_pem(): infile = testdata_path / "mnemonic.txt" outfile = testdata_out_path / "alice.pem" From 973521c6ad9edde071b45a3131ce621cc3d27517 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 11 Feb 2025 14:05:28 +0200 Subject: [PATCH 38/84] sign and verify message for validator wallet --- multiversx_sdk_cli/cli_wallet.py | 53 +++++++++++++++++---- multiversx_sdk_cli/sign_verify.py | 32 ++++++++++++- multiversx_sdk_cli/tests/test_cli_wallet.py | 45 +++++++++++++++++ 3 files changed, 119 insertions(+), 11 deletions(-) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 951a469b..125f1950 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -13,6 +13,7 @@ UserWallet, ValidatorPEM, ValidatorSecretKey, + ValidatorSigner, ) from multiversx_sdk.core.address import get_shard_of_pubkey @@ -25,7 +26,11 @@ KnownError, WalletGenerationError, ) -from multiversx_sdk_cli.sign_verify import SignedMessage, sign_message +from multiversx_sdk_cli.sign_verify import ( + SignedMessage, + sign_message, + sign_message_by_validator, +) from multiversx_sdk_cli.ux import show_critical_error, show_message logger = logging.getLogger("cli.wallet") @@ -151,11 +156,20 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub = cli_shared.add_command_subparser(subparsers, "wallet", "sign-message", "Sign a message") sub.add_argument("--message", required=True, help="the message you want to sign") - cli_shared.add_wallet_args(args, sub) + cli_shared.add_wallet_args(args=args, sub=sub, skip_required_check=True) + sub.add_argument("--validator-pem", required=False, type=str, help="the path to a validator pem file") + sub.add_argument( + "--validator-index", + required=False, + type=int, + default=0, + help="the index of the validator in case the file contains multiple validators (default: %(default)s)", + ) sub.set_defaults(func=sign_user_message) sub = cli_shared.add_command_subparser(subparsers, "wallet", "verify-message", "Verify a previously signed message") - sub.add_argument("--address", required=True, help="the bech32 address of the signer") + sub.add_argument("--address", help="the bech32 address of the signer") + sub.add_argument("--validator-pubkey", help="the hex string representing the validator's public key") sub.add_argument( "--message", required=True, @@ -399,19 +413,40 @@ def do_bech32(args: Any): def sign_user_message(args: Any): message: str = args.message - account = cli_shared.prepare_account(args) - signed_message = sign_message(message, account) + + if args.validator_pem: + path = Path(args.validator_pem).expanduser().resolve() + validator_signer = ValidatorSigner.from_pem_file(path, args.validator_index) + signed_message = sign_message_by_validator(message, validator_signer) + else: + account = cli_shared.prepare_account(args) + signed_message = sign_message(message, account) + utils.dump_out_json(signed_message.to_dictionary()) def verify_signed_message(args: Any): bech32_address = args.address + validator_pubkey = args.validator_pubkey message: str = args.message signature: str = args.signature - signed_message = SignedMessage(bech32_address, message, signature) - is_signed = signed_message.verify_signature() + if not bech32_address and not validator_pubkey: + raise BadUsage("You must provide either an address or a validator's public key") + + if bech32_address and validator_pubkey: + raise BadUsage("You must provide ONLY one: either an address or a validator's public key, not both") + + if validator_pubkey: + signed_message = SignedMessage(validator_pubkey, message, signature) + is_signed = signed_message.verify_validator_signature() + else: + signed_message = SignedMessage(bech32_address, message, signature) + is_signed = signed_message.verify_user_signature() + if is_signed: - show_message(f"""SUCCESS: The message "{message}" was signed by {bech32_address}""") + show_message(f"""SUCCESS: The message "{message}" was signed by {bech32_address or validator_pubkey}""") else: - show_critical_error(f"""FAILED: The message "{message}" was NOT signed by {bech32_address}""") + show_critical_error( + f"""FAILED: The message "{message}" was NOT signed by {bech32_address or validator_pubkey}""" + ) diff --git a/multiversx_sdk_cli/sign_verify.py b/multiversx_sdk_cli/sign_verify.py index 68724e00..c7af88a7 100644 --- a/multiversx_sdk_cli/sign_verify.py +++ b/multiversx_sdk_cli/sign_verify.py @@ -1,6 +1,14 @@ from typing import Dict, Protocol -from multiversx_sdk import Address, Message, MessageComputer, UserVerifier +from multiversx_sdk import ( + Address, + Message, + MessageComputer, + UserVerifier, + ValidatorPublicKey, + ValidatorSigner, + ValidatorVerifier, +) # fmt: off @@ -25,7 +33,7 @@ def __init__(self, address: str, message: str, signature: str) -> None: self.signature = signature - def verify_signature(self) -> bool: + def verify_user_signature(self) -> bool: verifiable_message = Message(self.message.encode()) verifiable_message.signature = bytes.fromhex(self.signature) message_computer = MessageComputer() @@ -37,6 +45,19 @@ def verify_signature(self) -> bool: ) return is_signed + def verify_validator_signature(self) -> bool: + verifiable_message = Message(self.message.encode()) + verifiable_message.signature = bytes.fromhex(self.signature) + message_computer = MessageComputer() + + validator_pubkey = ValidatorPublicKey(bytes.fromhex(self.address)) + verifier = ValidatorVerifier(validator_pubkey) + is_signed = verifier.verify( + message_computer.compute_bytes_for_signing(verifiable_message), + verifiable_message.signature, + ) + return is_signed + def to_dictionary(self) -> Dict[str, str]: return { "address": self.address, @@ -48,3 +69,10 @@ def to_dictionary(self) -> Dict[str, str]: def sign_message(message: str, account: IAccount) -> SignedMessage: signature = account.sign_message(Message(message.encode())) return SignedMessage(account.address.to_bech32(), message, signature.hex()) + + +def sign_message_by_validator(message: str, validator: ValidatorSigner) -> SignedMessage: + message_computer = MessageComputer() + serialized_message = message_computer.compute_bytes_for_signing(Message(message.encode())) + signature = validator.sign(serialized_message) + return SignedMessage(validator.get_pubkey().hex(), message, signature.hex()) diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index a11b7f9f..c5bf5022 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -144,6 +144,7 @@ def test_create_validator_wallet(): assert return_code path = testdata_out_path / "validator.pem" + path.unlink(missing_ok=True) return_code = main( [ "wallet", @@ -392,6 +393,50 @@ def test_wallet_sign_message(capsys: Any): } +def test_validator_sign_and_verify_message(capsys: Any): + message = "test" + validator = testdata_path / "validator_01.pem" + + return_code = main( + [ + "wallet", + "sign-message", + "--message", + message, + "--validator-pem", + str(validator), + ] + ) + assert not return_code + + out = json.loads(_read_stdout(capsys)) + assert out == { + "address": "f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d", + "message": "test", + "signature": "0xeb06700ee0d9c083560f2312a12962fd95400a21f6b109721a5f937ad5ec21efbe5312f925aa16f2de4d24799cd04c91", + } + + # Clear the captured content + capsys.readouterr() + + return_code = main( + [ + "wallet", + "verify-message", + "--validator-pubkey", + "f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d", + "--message", + message, + "--signature", + "0xeb06700ee0d9c083560f2312a12962fd95400a21f6b109721a5f937ad5ec21efbe5312f925aa16f2de4d24799cd04c91", + ] + ) + assert not return_code + out = _read_stdout(capsys) + text = """SUCCESS: The message "test" was signed by f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d""".split() + assert all(out.find(word) for word in text) + + def test_verify_previously_signed_message(capsys: Any): message = "test" address = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" From f0466271125336415378f4d4b4f835a315702e89 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 12 Feb 2025 11:11:56 +0200 Subject: [PATCH 39/84] fixes after review --- .github/workflows/mypy.yml | 2 + multiversx_sdk_cli/cli_validators.py | 246 +++++------------- multiversx_sdk_cli/config.py | 2 +- multiversx_sdk_cli/tests/test_cli_dns.py | 3 +- .../tests/test_cli_validators.py | 3 +- multiversx_sdk_cli/validators/core.py | 2 +- 6 files changed, 73 insertions(+), 185 deletions(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index c2afd3e7..a82a8196 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -8,6 +8,8 @@ jobs: - uses: actions/checkout@v2 - name: Install Python dependencies uses: py-actions/py-dependency-install@v2 + - name: Install dev dependencies + run: pip install -r requirements-dev.txt - uses: tsuyoshicho/action-mypy@v3 with: github_token: ${{ secrets.github_token }} diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 252a817c..eae67200 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -139,22 +139,10 @@ def _add_nodes_arg(sub: Any): def do_stake(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) gas_limit = 0 if args.estimate_gas else args.gas_limit @@ -203,23 +191,39 @@ def do_stake(args: Any): cli_shared.send_or_simulate(tx, args) -def do_unstake(args: Any): +def validate_args(args: Any) -> None: cli_shared.check_guardian_args(args) cli_shared.check_broadcast_args(args) cli_shared.prepare_chain_id_in_args(args) + +def prepare_sender(args: Any): sender = cli_shared.prepare_account(args) + nonce = ( + int(args.nonce) + if args.nonce is not None + else cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + ) + return sender, nonce - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) +def prepare_guardian(args: Any): guardian = cli_shared.load_guardian_account(args) guardian_address = cli_shared.get_guardian_address(guardian, args) + return guardian, guardian_address + +def prepare_relayer(args: Any): relayer = cli_shared.load_relayer_account(args) relayer_address = cli_shared.get_relayer_address(relayer, args) + return relayer, relayer_address + + +def do_unstake(args: Any): + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) gas_limit = 0 if args.estimate_gas else args.gas_limit @@ -248,22 +252,10 @@ def do_unstake(args: Any): def do_unjail(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) gas_limit = 0 if args.estimate_gas else args.gas_limit @@ -292,22 +284,10 @@ def do_unjail(args: Any): def do_unbond(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) gas_limit = 0 if args.estimate_gas else args.gas_limit @@ -336,22 +316,10 @@ def do_unbond(args: Any): def change_reward_address(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) gas_limit = 0 if args.estimate_gas else args.gas_limit @@ -380,22 +348,10 @@ def change_reward_address(args: Any): def do_claim(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) gas_limit = 0 if args.estimate_gas else args.gas_limit @@ -422,22 +378,10 @@ def do_claim(args: Any): def do_unstake_nodes(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) gas_limit = 0 if args.estimate_gas else args.gas_limit @@ -467,22 +411,10 @@ def do_unstake_nodes(args: Any): def do_unstake_tokens(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) value = int(args.unstake_value) @@ -511,22 +443,10 @@ def do_unstake_tokens(args: Any): def do_unbond_nodes(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) gas_limit = 0 if args.estimate_gas else args.gas_limit @@ -556,22 +476,10 @@ def do_unbond_nodes(args: Any): def do_unbond_tokens(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) value = int(args.unbond_value) @@ -600,22 +508,10 @@ def do_unbond_tokens(args: Any): def do_clean_registered_data(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) gas_limit = 0 if args.estimate_gas else args.gas_limit @@ -642,22 +538,10 @@ def do_clean_registered_data(args: Any): def do_restake_unstaked_nodes(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + validate_args(args) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) native_amount = int(args.value) gas_limit = 0 if args.estimate_gas else args.gas_limit diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 67e55d24..afcf82b2 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -25,7 +25,7 @@ class MetaChainSystemSCsCost: UNSTAKE_TOKENS = 5000000 UNBOND_TOKENS = 5000000 CLEAN_REGISTERED_DATA = 5000000 - RE_STAKE_UNSTAKED_NODES = 5000000 + RESTAKE_UNSTAKED_NODES = 5000000 def get_dependency_resolution(key: str) -> str: diff --git a/multiversx_sdk_cli/tests/test_cli_dns.py b/multiversx_sdk_cli/tests/test_cli_dns.py index 63aacccc..9241e393 100644 --- a/multiversx_sdk_cli/tests/test_cli_dns.py +++ b/multiversx_sdk_cli/tests/test_cli_dns.py @@ -63,4 +63,5 @@ def get_output(capsys: Any): def _read_stdout(capsys: Any) -> str: - return capsys.readouterr().out.strip() + stdout: str = capsys.readouterr().out.strip() + return stdout diff --git a/multiversx_sdk_cli/tests/test_cli_validators.py b/multiversx_sdk_cli/tests/test_cli_validators.py index 1632c3b4..a6bff22a 100644 --- a/multiversx_sdk_cli/tests/test_cli_validators.py +++ b/multiversx_sdk_cli/tests/test_cli_validators.py @@ -612,4 +612,5 @@ def get_output(capsys: Any): def _read_stdout(capsys: Any) -> str: - return capsys.readouterr().out.strip() + stdout: str = capsys.readouterr().out.strip() + return stdout diff --git a/multiversx_sdk_cli/validators/core.py b/multiversx_sdk_cli/validators/core.py index 7a8df43d..e7c6e194 100644 --- a/multiversx_sdk_cli/validators/core.py +++ b/multiversx_sdk_cli/validators/core.py @@ -598,7 +598,7 @@ def create_transaction_for_restaking_unstaked_nodes( data = f"reStakeUnStakedNodes{parsed_keys}" if estimate_gas: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.RE_STAKE_UNSTAKED_NODES, num_keys) + gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.RESTAKE_UNSTAKED_NODES, num_keys) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) From 1705f7eb08bd71495cf20c5854e82f95760914d3 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 12 Feb 2025 14:56:08 +0200 Subject: [PATCH 40/84] fix get account --- multiversx_sdk_cli/cli_accounts.py | 41 ++++++++++++++++++++++++++++-- multiversx_sdk_cli/cli_shared.py | 2 +- multiversx_sdk_cli/utils.py | 2 +- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/multiversx_sdk_cli/cli_accounts.py b/multiversx_sdk_cli/cli_accounts.py index d153b114..1877fc39 100644 --- a/multiversx_sdk_cli/cli_accounts.py +++ b/multiversx_sdk_cli/cli_accounts.py @@ -1,7 +1,12 @@ import logging from typing import Any -from multiversx_sdk import Address, ProxyNetworkProvider +from multiversx_sdk import ( + AccountOnNetwork, + Address, + BlockCoordinates, + ProxyNetworkProvider, +) from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.config import get_config_for_network_providers @@ -32,6 +37,8 @@ def _add_address_arg(sub: Any): def get_account(args: Any): + omitted_fields = cli_shared.parse_omit_fields_arg(args) + proxy_url = args.proxy address = args.address config = get_config_for_network_providers() @@ -45,4 +52,34 @@ def get_account(args: Any): elif args.username: print(account.username) else: - utils.dump_out_json(account.raw) + account = _account_on_network_to_dictionary(account) + utils.dump_out_json(utils.omit_fields(account, omitted_fields)) + + +def _account_on_network_to_dictionary(account: AccountOnNetwork) -> dict[str, Any]: + return { + "address": account.address.to_bech32(), + "nonce": account.nonce, + "balance": account.balance, + "is_guarded": account.is_guarded, + "username": account.username, + "block_coordinates": ( + _block_coordinates_to_dictionary(account.block_coordinates) if account.block_coordinates else {} + ), + "contract_code_hash": account.contract_code_hash.hex(), + "contract_code": account.contract_code.hex(), + "contract_developer_reward": account.contract_developer_reward, + "contract_owner_address": account.contract_owner_address.to_bech32() if account.contract_owner_address else "", + "is_contract_upgradable": account.is_contract_upgradable, + "is_contract_readable": account.is_contract_readable, + "is_contract_payable": account.is_contract_payable, + "is_contract_payable_by_contract": account.is_contract_payable_by_contract, + } + + +def _block_coordinates_to_dictionary(block: BlockCoordinates) -> dict[str, Any]: + return { + "nonce": block.nonce, + "hash": block.hash.hex(), + "root_hash": block.root_hash.hex(), + } diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index aa5eaefa..71b9a0d2 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -292,7 +292,7 @@ def add_omit_fields_arg(sub: Any): default="[]", type=str, required=False, - help="omit fields in the output payload (default: %(default)s)", + help="omit fields in the output payload (default: %(default)s); fields should be passed as a string containing a list of fields (e.g. \"['field1', 'field2']\")", ) diff --git a/multiversx_sdk_cli/utils.py b/multiversx_sdk_cli/utils.py index 11c5ef36..c40eeba7 100644 --- a/multiversx_sdk_cli/utils.py +++ b/multiversx_sdk_cli/utils.py @@ -44,7 +44,7 @@ def default(self, o: Any) -> Any: return super().default(o) -def omit_fields(data: Any, fields: List[str] = []): +def omit_fields(data: Any, fields: List[str] = []) -> dict[str, Any]: if isinstance(data, dict): for field in fields: data.pop(field, None) From 45efd0ef5c893a4e6bc080522e8eb1397a85452c Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 12 Feb 2025 15:27:50 +0200 Subject: [PATCH 41/84] fix get transaction --- multiversx_sdk_cli/cli_output.py | 21 +++++----- multiversx_sdk_cli/cli_transactions.py | 9 ++-- multiversx_sdk_cli/transactions.py | 58 +++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/multiversx_sdk_cli/cli_output.py b/multiversx_sdk_cli/cli_output.py index eed1e76a..2250f489 100644 --- a/multiversx_sdk_cli/cli_output.py +++ b/multiversx_sdk_cli/cli_output.py @@ -1,11 +1,12 @@ import json import logging from collections import OrderedDict -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union from multiversx_sdk import Address, Transaction, TransactionOnNetwork from multiversx_sdk_cli import utils +from multiversx_sdk_cli.transactions import transaction_on_network_to_dictionary from multiversx_sdk_cli.utils import ISerializable logger = logging.getLogger("cli.output") @@ -15,17 +16,17 @@ class CLIOutputBuilder: def __init__(self) -> None: self.emitted_transaction_hash: Optional[str] = None self.emitted_transaction: Union[Transaction, None] = None - self.emitted_transaction_omitted_fields: List[str] = [] + self.emitted_transaction_omitted_fields: list[str] = [] self.contract_address: Union[Address, None] = None self.transaction_on_network: Union[TransactionOnNetwork, None] = None - self.transaction_on_network_omitted_fields: List[str] = [] + self.transaction_on_network_omitted_fields: list[str] = [] self.simulation_results: Union[ISerializable, None] = None def set_emitted_transaction_hash(self, hash: str): self.emitted_transaction_hash = hash return self - def set_emitted_transaction(self, emitted_transaction: Transaction, omitted_fields: List[str] = []): + def set_emitted_transaction(self, emitted_transaction: Transaction, omitted_fields: list[str] = []): self.emitted_transaction = emitted_transaction self.emitted_transaction_omitted_fields = omitted_fields return self @@ -34,13 +35,13 @@ def set_contract_address(self, contract_address: Address): self.contract_address = contract_address return self - def set_awaited_transaction(self, awaited_transaction: TransactionOnNetwork, omitted_fields: List[str] = []): + def set_awaited_transaction(self, awaited_transaction: TransactionOnNetwork, omitted_fields: list[str] = []): return self.set_transaction_on_network(awaited_transaction, omitted_fields) def set_transaction_on_network( self, transaction_on_network: TransactionOnNetwork, - omitted_fields: List[str] = [], + omitted_fields: list[str] = [], ): self.transaction_on_network = transaction_on_network self.transaction_on_network_omitted_fields = omitted_fields @@ -50,8 +51,8 @@ def set_simulation_results(self, simulation_results: ISerializable): self.simulation_results = simulation_results return self - def build(self) -> Dict[str, Any]: - output: Dict[str, Any] = OrderedDict() + def build(self) -> dict[str, Any]: + output: dict[str, Any] = OrderedDict() if self.emitted_transaction: emitted_transaction_dict = self.emitted_transaction.to_dictionary() @@ -68,7 +69,7 @@ def build(self) -> Dict[str, Any]: output["contractAddress"] = contract_address if self.transaction_on_network: - transaction_on_network_dict = self.transaction_on_network.raw + transaction_on_network_dict = transaction_on_network_to_dictionary(self.transaction_on_network) utils.omit_fields(transaction_on_network_dict, self.transaction_on_network_omitted_fields) output["transactionOnNetwork"] = transaction_on_network_dict @@ -85,7 +86,7 @@ def describe( with_transaction_on_network: bool = False, with_simulation: bool = False, ) -> str: - output: Dict[str, Any] = OrderedDict() + output: dict[str, Any] = OrderedDict() if with_emitted: output["emittedTransaction"] = { diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 2bb54814..399cfe35 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -73,12 +73,6 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: f"Get a transaction.{CLIOutputBuilder.describe(with_emitted=False, with_transaction_on_network=True)}", ) sub.add_argument("--hash", required=True, help="the hash") - sub.add_argument("--sender", required=False, help="the sender address") - sub.add_argument( - "--with-results", - action="store_true", - help="will also return the results of transaction", - ) cli_shared.add_proxy_arg(sub) cli_shared.add_omit_fields_arg(sub) sub.set_defaults(func=get_transaction) @@ -203,6 +197,9 @@ def send_transaction(args: Any): def get_transaction(args: Any): + if not args.proxy: + raise BadUsage("Proxy argument not provided") + omit_fields = cli_shared.parse_omit_fields_arg(args) config = get_config_for_network_providers() diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index fb5bf164..597f7c29 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -1,12 +1,15 @@ import json import logging import time -from typing import Optional, Protocol, TextIO, Union +from typing import Any, Optional, Protocol, TextIO, Union from multiversx_sdk import ( Address, + SmartContractResult, TokenTransfer, Transaction, + TransactionEvent, + TransactionLogs, TransactionOnNetwork, TransactionsFactoryConfig, TransferTransactionsFactory, @@ -122,3 +125,56 @@ def load_transaction_from_file(f: TextIO) -> Transaction: data_json: bytes = f.read().encode() transaction_dictionary = json.loads(data_json).get("tx") or json.loads(data_json).get("emittedTransaction") return Transaction.new_from_dictionary(transaction_dictionary) + + +def transaction_event_to_dictionary(event: TransactionEvent) -> dict[str, Any]: + return { + "address": event.address.to_bech32(), + "identifier": event.identifier, + "topics": [topic.hex() for topic in event.topics], + "data": event.data.decode(), + "additional_data": [data.decode() for data in event.additional_data], + } + + +def transaction_logs_to_dictionary(logs: TransactionLogs) -> dict[str, Any]: + return { + "address": logs.address.to_bech32(), + "events": [transaction_event_to_dictionary(event) for event in logs.events], + } + + +def smart_contract_result_to_dictionary(result: SmartContractResult) -> dict[str, Any]: + return { + "sender": result.sender.to_bech32(), + "receiver": result.receiver.to_bech32(), + "data": result.data.decode(), + "logs": transaction_logs_to_dictionary(result.logs), + } + + +def transaction_on_network_to_dictionary(tx: TransactionOnNetwork) -> dict[str, Any]: + return { + "sender": tx.sender.to_bech32(), + "receiver": tx.receiver.to_bech32(), + "hash": tx.hash.hex(), + "nonce": tx.nonce, + "round": tx.round, + "epoch": tx.epoch, + "timestamp": tx.timestamp, + "block_hash": tx.block_hash.hex(), + "miniblock_hash": tx.miniblock_hash.hex(), + "sender_shard": tx.sender_shard, + "receiver_shard": tx.receiver_shard, + "value": tx.value, + "gas_limit": tx.gas_limit, + "gas_price": tx.gas_price, + "function": tx.function, + "data": tx.data.decode(), + "version": tx.version, + "options": tx.options, + "signature": tx.signature.hex(), + "status": tx.status.status, + "smart_contract_results": [smart_contract_result_to_dictionary(res) for res in tx.smart_contract_results], + "logs": transaction_logs_to_dictionary(tx.logs), + } From dbea15ca223c2af70804398349fdcf5ea825d8ee Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 13 Feb 2025 14:32:38 +0200 Subject: [PATCH 42/84] move validator wallet to separate command --- multiversx_sdk_cli/cli.py | 2 + multiversx_sdk_cli/cli_validator_wallet.py | 123 +++++++++++++++++ multiversx_sdk_cli/cli_wallet.py | 95 ++----------- multiversx_sdk_cli/sign_verify.py | 21 +-- .../tests/test_cli_validator_wallet.py | 128 ++++++++++++++++++ multiversx_sdk_cli/tests/test_cli_wallet.py | 84 +----------- 6 files changed, 271 insertions(+), 182 deletions(-) create mode 100644 multiversx_sdk_cli/cli_validator_wallet.py create mode 100644 multiversx_sdk_cli/tests/test_cli_validator_wallet.py diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index a4eafff4..0970ff97 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -20,6 +20,7 @@ import multiversx_sdk_cli.cli_ledger import multiversx_sdk_cli.cli_localnet import multiversx_sdk_cli.cli_transactions +import multiversx_sdk_cli.cli_validator_wallet import multiversx_sdk_cli.cli_validators import multiversx_sdk_cli.cli_wallet import multiversx_sdk_cli.version @@ -112,6 +113,7 @@ def setup_parser(args: List[str]): commands.append(multiversx_sdk_cli.cli_accounts.setup_parser(subparsers)) commands.append(multiversx_sdk_cli.cli_ledger.setup_parser(subparsers)) commands.append(multiversx_sdk_cli.cli_wallet.setup_parser(args, subparsers)) + commands.append(multiversx_sdk_cli.cli_validator_wallet.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_deps.setup_parser(subparsers)) commands.append(multiversx_sdk_cli.cli_config.setup_parser(subparsers)) commands.append(multiversx_sdk_cli.cli_localnet.setup_parser(args, subparsers)) diff --git a/multiversx_sdk_cli/cli_validator_wallet.py b/multiversx_sdk_cli/cli_validator_wallet.py new file mode 100644 index 00000000..2f101886 --- /dev/null +++ b/multiversx_sdk_cli/cli_validator_wallet.py @@ -0,0 +1,123 @@ +import logging +from pathlib import Path +from typing import Any + +from multiversx_sdk import ValidatorPEM, ValidatorSecretKey, ValidatorSigner + +from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.errors import BadUserInput +from multiversx_sdk_cli.sign_verify import SignedMessage, sign_message_by_validator +from multiversx_sdk_cli.ux import show_critical_error, show_message + +logger = logging.getLogger("cli.validator_wallet") + + +def setup_parser(args: list[str], subparsers: Any) -> Any: + parser = cli_shared.add_group_subparser( + subparsers, + "validator-wallet", + "Create a validator wallet, sign and verify messages and convert a validator wallet to a hex secret key.", + ) + subparsers = parser.add_subparsers() + + sub = cli_shared.add_command_subparser( + subparsers, + "validator-wallet", + "new", + "Create a new validator wallet and save it as a PEM file.", + ) + sub.add_argument( + "--outfile", + help="the output path and file name for the generated wallet", + type=str, + required=True, + ) + sub.set_defaults(func=create_new_wallet) + + sub = cli_shared.add_command_subparser(subparsers, "validator-wallet", "sign-message", "Sign a message.") + sub.add_argument("--message", required=True, help="the message you want to sign") + sub.add_argument("--pem", required=True, type=str, help="the path to a validator pem file") + sub.add_argument( + "--validator-index", + required=False, + type=int, + default=0, + help="the index of the validator in case the file contains multiple validators (default: %(default)s)", + ) + sub.set_defaults(func=sign_message) + + sub = cli_shared.add_command_subparser( + subparsers, "validator-wallet", "verify-message-signature", "Verify a previously signed message." + ) + sub.add_argument("--pubkey", required=True, help="the hex string representing the validator's public key") + sub.add_argument( + "--message", + required=True, + help="the previously signed message(readable text, as it was signed)", + ) + sub.add_argument("--signature", required=True, help="the signature in hex format") + sub.set_defaults(func=verify_message_signature) + + sub = cli_shared.add_command_subparser( + subparsers, "validator-wallet", "convert", "Convert a validator pem file to a hex secret key." + ) + sub.add_argument("--infile", required=True, help="the pem file of the wallet") + sub.add_argument( + "--index", + required=False, + type=int, + default=0, + help="the index of the validator in case the file contains multiple validators (default: %(default)s)", + ) + sub.set_defaults(func=convert_wallet_to_secret_key) + + parser.epilog = cli_shared.build_group_epilog(subparsers) + return subparsers + + +def create_new_wallet(args: Any): + path = Path(args.outfile).expanduser().resolve() + + if path.exists(): + raise BadUserInput(f"File already exists, will not overwrite: {str(path)}") + + secret_key = ValidatorSecretKey.generate() + public_key = secret_key.generate_public_key() + validator_pem = ValidatorPEM(label=public_key.hex(), secret_key=secret_key) + validator_pem.save(path) + + logger.info(f"Validator wallet saved: {str(path)}") + + +def sign_message(args: Any): + path = Path(args.pem).expanduser().resolve() + validator_signer = ValidatorSigner.from_pem_file(path, args.validator_index) + signed_message = sign_message_by_validator(args.message, validator_signer) + + utils.dump_out_json(signed_message.to_dictionary()) + + +def verify_message_signature(args: Any): + message = args.message + pubkey = args.pubkey + + signed_message = SignedMessage(pubkey, message, args.signature) + is_signed = signed_message.verify_validator_signature() + + if is_signed: + show_message(f"""SUCCESS: The message "{message}" was signed by {pubkey}""") + else: + show_critical_error(f"""FAILED: The message "{message}" was NOT signed by {pubkey}""") + + +def convert_wallet_to_secret_key(args: Any): + file = args.infile + index = args.index + + path = Path(file).expanduser().resolve() + if not path.is_file(): + raise BadUserInput("File not found") + + signer = ValidatorSigner.from_pem_file(path, index) + print(f"Public key: {signer.get_pubkey().hex()}") + print(f"Secret key: {signer.secret_key.hex()}") diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 125f1950..ed744061 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -3,18 +3,9 @@ import logging import sys from pathlib import Path -from typing import Any, Optional, Union - -from multiversx_sdk import ( - Address, - Mnemonic, - UserPEM, - UserSecretKey, - UserWallet, - ValidatorPEM, - ValidatorSecretKey, - ValidatorSigner, -) +from typing import Any, Optional + +from multiversx_sdk import Address, Mnemonic, UserPEM, UserSecretKey, UserWallet from multiversx_sdk.core.address import get_shard_of_pubkey from multiversx_sdk_cli import cli_shared, utils @@ -26,11 +17,7 @@ KnownError, WalletGenerationError, ) -from multiversx_sdk_cli.sign_verify import ( - SignedMessage, - sign_message, - sign_message_by_validator, -) +from multiversx_sdk_cli.sign_verify import SignedMessage, sign_message from multiversx_sdk_cli.ux import show_critical_error, show_message logger = logging.getLogger("cli.wallet") @@ -75,12 +62,6 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "new", "Create a new wallet and print its mnemonic; optionally save as password-protected JSON (recommended) or PEM (not recommended)", ) - sub.add_argument( - "--validator-wallet", - help="Create a validator wallet. If flag is provided, only the `pem` format is supported. Address hrp and shard will be ignored if provided.", - action="store_true", - default=False, - ) sub.add_argument( "--format", choices=WALLET_FORMATS, @@ -157,19 +138,10 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub = cli_shared.add_command_subparser(subparsers, "wallet", "sign-message", "Sign a message") sub.add_argument("--message", required=True, help="the message you want to sign") cli_shared.add_wallet_args(args=args, sub=sub, skip_required_check=True) - sub.add_argument("--validator-pem", required=False, type=str, help="the path to a validator pem file") - sub.add_argument( - "--validator-index", - required=False, - type=int, - default=0, - help="the index of the validator in case the file contains multiple validators (default: %(default)s)", - ) sub.set_defaults(func=sign_user_message) sub = cli_shared.add_command_subparser(subparsers, "wallet", "verify-message", "Verify a previously signed message") - sub.add_argument("--address", help="the bech32 address of the signer") - sub.add_argument("--validator-pubkey", help="the hex string representing the validator's public key") + sub.add_argument("--address", required=True, help="the bech32 address of the signer") sub.add_argument( "--message", required=True, @@ -188,11 +160,6 @@ def wallet_new(args: Any): address_hrp = args.address_hrp shard = args.shard - if args.validator_wallet: - _ensure_correct_arguments_for_validator_wallet(format, outfile) - _generate_validator_wallet(outfile) - return - if shard is not None: mnemonic = _generate_mnemonic_with_shard_constraint(shard) else: @@ -233,28 +200,6 @@ def wallet_new(args: Any): logger.info(f"Wallet ({format}) saved: {outfile}") -def _generate_validator_wallet(outfile: str): - path = Path(outfile).expanduser().resolve() - secret_key = ValidatorSecretKey.generate() - public_key = secret_key.generate_public_key() - validator_pem = ValidatorPEM(label=public_key.hex(), secret_key=secret_key) - validator_pem.save(path) - - logger.info(f"Validator wallet saved: {str(path)}") - - -def _ensure_correct_arguments_for_validator_wallet(format: str, outfile: Union[str, None]): - if format != WALLET_FORMAT_PEM: - raise BadUsage("Only PEM format supported for validator wallet") - - if outfile is None: - raise BadUsage("The `--outfile` argument is required") - - path = Path(outfile).expanduser().resolve() - if path.exists(): - raise BadUserInput(f"File already exists, will not overwrite: {str(path)}") - - def _generate_mnemonic_with_shard_constraint(shard: int) -> Mnemonic: if shard not in CURRENT_SHARDS: raise BadUserInput(f"Wrong shard provided. Choose between {CURRENT_SHARDS}") @@ -414,39 +359,21 @@ def do_bech32(args: Any): def sign_user_message(args: Any): message: str = args.message - if args.validator_pem: - path = Path(args.validator_pem).expanduser().resolve() - validator_signer = ValidatorSigner.from_pem_file(path, args.validator_index) - signed_message = sign_message_by_validator(message, validator_signer) - else: - account = cli_shared.prepare_account(args) - signed_message = sign_message(message, account) + account = cli_shared.prepare_account(args) + signed_message = sign_message(message, account) utils.dump_out_json(signed_message.to_dictionary()) def verify_signed_message(args: Any): bech32_address = args.address - validator_pubkey = args.validator_pubkey message: str = args.message signature: str = args.signature - if not bech32_address and not validator_pubkey: - raise BadUsage("You must provide either an address or a validator's public key") - - if bech32_address and validator_pubkey: - raise BadUsage("You must provide ONLY one: either an address or a validator's public key, not both") - - if validator_pubkey: - signed_message = SignedMessage(validator_pubkey, message, signature) - is_signed = signed_message.verify_validator_signature() - else: - signed_message = SignedMessage(bech32_address, message, signature) - is_signed = signed_message.verify_user_signature() + signed_message = SignedMessage(bech32_address, message, signature) + is_signed = signed_message.verify_user_signature() if is_signed: - show_message(f"""SUCCESS: The message "{message}" was signed by {bech32_address or validator_pubkey}""") + show_message(f"""SUCCESS: The message "{message}" was signed by {bech32_address}""") else: - show_critical_error( - f"""FAILED: The message "{message}" was NOT signed by {bech32_address or validator_pubkey}""" - ) + show_critical_error(f"""FAILED: The message "{message}" was NOT signed by {bech32_address}""") diff --git a/multiversx_sdk_cli/sign_verify.py b/multiversx_sdk_cli/sign_verify.py index c7af88a7..a380fbb9 100644 --- a/multiversx_sdk_cli/sign_verify.py +++ b/multiversx_sdk_cli/sign_verify.py @@ -1,4 +1,4 @@ -from typing import Dict, Protocol +from typing import Protocol from multiversx_sdk import ( Address, @@ -7,7 +7,6 @@ UserVerifier, ValidatorPublicKey, ValidatorSigner, - ValidatorVerifier, ) @@ -46,19 +45,13 @@ def verify_user_signature(self) -> bool: return is_signed def verify_validator_signature(self) -> bool: - verifiable_message = Message(self.message.encode()) - verifiable_message.signature = bytes.fromhex(self.signature) - message_computer = MessageComputer() - validator_pubkey = ValidatorPublicKey(bytes.fromhex(self.address)) - verifier = ValidatorVerifier(validator_pubkey) - is_signed = verifier.verify( - message_computer.compute_bytes_for_signing(verifiable_message), - verifiable_message.signature, + return validator_pubkey.verify( + self.message.encode(), + bytes.fromhex(self.signature), ) - return is_signed - def to_dictionary(self) -> Dict[str, str]: + def to_dictionary(self) -> dict[str, str]: return { "address": self.address, "message": self.message, @@ -72,7 +65,5 @@ def sign_message(message: str, account: IAccount) -> SignedMessage: def sign_message_by_validator(message: str, validator: ValidatorSigner) -> SignedMessage: - message_computer = MessageComputer() - serialized_message = message_computer.compute_bytes_for_signing(Message(message.encode())) - signature = validator.sign(serialized_message) + signature = validator.sign(message.encode()) return SignedMessage(validator.get_pubkey().hex(), message, signature.hex()) diff --git a/multiversx_sdk_cli/tests/test_cli_validator_wallet.py b/multiversx_sdk_cli/tests/test_cli_validator_wallet.py new file mode 100644 index 00000000..a3f91771 --- /dev/null +++ b/multiversx_sdk_cli/tests/test_cli_validator_wallet.py @@ -0,0 +1,128 @@ +import json +from pathlib import Path +from typing import Any + +from multiversx_sdk import ValidatorPEM + +from multiversx_sdk_cli.cli import main + +testdata_path = Path(__file__).parent / "testdata" +testdata_out_path = Path(__file__).parent / "testdata-out" + + +def test_create_validator_wallet(): + outfile = testdata_out_path / "validator.pem" + outfile.unlink(missing_ok=True) + + return_code = main( + [ + "validator-wallet", + "new", + "--outfile", + str(outfile), + ] + ) + assert not return_code + assert outfile.is_file() + + wallet = ValidatorPEM.from_file(outfile) + assert wallet.label + assert wallet.secret_key + + +def test_validator_sign_and_verify_message(capsys: Any): + message = "test" + validator = testdata_path / "validator_01.pem" + + return_code = main( + [ + "validator-wallet", + "sign-message", + "--message", + message, + "--pem", + str(validator), + ] + ) + assert not return_code + + out = json.loads(_read_stdout(capsys)) + assert out == { + "address": "f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d", + "message": "test", + "signature": "0x1c1dc0f6ef4f7c2a335cabd1da4bf3f333902c90ad9ecff0873854453419cd092f490238fb2f1bc6bf9f89337dea188f", + } + + # Clear the captured content + capsys.readouterr() + + return_code = main( + [ + "validator-wallet", + "verify-message-signature", + "--pubkey", + "f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d", + "--message", + message, + "--signature", + "0x1c1dc0f6ef4f7c2a335cabd1da4bf3f333902c90ad9ecff0873854453419cd092f490238fb2f1bc6bf9f89337dea188f", + ] + ) + assert not return_code + out = _read_stdout(capsys) + + success = "SUCCESS:" + assert success in out.split() + + # repeate signature check with invalid signature + # Clear the captured content + capsys.readouterr() + + return_code = main( + [ + "validator-wallet", + "verify-message-signature", + "--pubkey", + "f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d", + "--message", + message, + "--signature", + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + ] + ) + assert not return_code + out = _read_stdout(capsys) + + success = "SUCCESS:" + assert success not in out.split() + + fail = "FAILED:" + assert fail in out.split() + + +def test_validator_wallet_convert_to_hex_secret_key(capsys: Any): + infile = testdata_path / "validator_01.pem" + + return_code = main( + [ + "validator-wallet", + "convert", + "--infile", + str(infile), + ] + ) + assert not return_code + + output = _read_stdout(capsys) + lines = output.splitlines() + + assert ( + lines[0] + == "Public key: f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d" + ) + assert lines[1] == "Secret key: 7c19bf3a0c57cdd1fb08e4607cebaa3647d6b9261b4693f61e96e54b218d442a" + + +def _read_stdout(capsys: Any) -> str: + stdout: str = capsys.readouterr().out.strip() + return stdout diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index c5bf5022..50605851 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -3,14 +3,7 @@ from pathlib import Path from typing import Any -from multiversx_sdk import ( - Address, - AddressComputer, - Mnemonic, - UserPEM, - UserWallet, - ValidatorPEM, -) +from multiversx_sdk import Address, AddressComputer, Mnemonic, UserPEM, UserWallet from multiversx_sdk_cli.cli import main @@ -133,37 +126,6 @@ def test_wallet_new_as_keystore_with_secret_key(capsys: Any, monkeypatch: Any): assert actual_secret_key.hex() == expected_secret_key.hex() -def test_create_validator_wallet(): - return_code = main(["wallet", "new", "--validator-wallet"]) - assert return_code - - return_code = main(["wallet", "new", "--validator-wallet", "--format", "pem"]) - assert return_code - - return_code = main(["wallet", "new", "--validator-wallet", "--outfile", str(testdata_out_path / "validator.pem")]) - assert return_code - - path = testdata_out_path / "validator.pem" - path.unlink(missing_ok=True) - return_code = main( - [ - "wallet", - "new", - "--validator-wallet", - "--format", - "pem", - "--outfile", - str(path), - ] - ) - assert not return_code - assert path.is_file() - - wallet = ValidatorPEM.from_file(path) - assert wallet.label - assert wallet.secret_key - - def test_wallet_convert_raw_mnemonic_to_pem(): infile = testdata_path / "mnemonic.txt" outfile = testdata_out_path / "alice.pem" @@ -393,50 +355,6 @@ def test_wallet_sign_message(capsys: Any): } -def test_validator_sign_and_verify_message(capsys: Any): - message = "test" - validator = testdata_path / "validator_01.pem" - - return_code = main( - [ - "wallet", - "sign-message", - "--message", - message, - "--validator-pem", - str(validator), - ] - ) - assert not return_code - - out = json.loads(_read_stdout(capsys)) - assert out == { - "address": "f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d", - "message": "test", - "signature": "0xeb06700ee0d9c083560f2312a12962fd95400a21f6b109721a5f937ad5ec21efbe5312f925aa16f2de4d24799cd04c91", - } - - # Clear the captured content - capsys.readouterr() - - return_code = main( - [ - "wallet", - "verify-message", - "--validator-pubkey", - "f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d", - "--message", - message, - "--signature", - "0xeb06700ee0d9c083560f2312a12962fd95400a21f6b109721a5f937ad5ec21efbe5312f925aa16f2de4d24799cd04c91", - ] - ) - assert not return_code - out = _read_stdout(capsys) - text = """SUCCESS: The message "test" was signed by f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d""".split() - assert all(out.find(word) for word in text) - - def test_verify_previously_signed_message(capsys: Any): message = "test" address = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" From c540b59cd43a145831a6bc14505bb717a1d0811d Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 13 Feb 2025 15:44:02 +0200 Subject: [PATCH 43/84] rename argument --- multiversx_sdk_cli/cli_validator_wallet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk_cli/cli_validator_wallet.py b/multiversx_sdk_cli/cli_validator_wallet.py index 2f101886..9415a2c9 100644 --- a/multiversx_sdk_cli/cli_validator_wallet.py +++ b/multiversx_sdk_cli/cli_validator_wallet.py @@ -38,7 +38,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument("--message", required=True, help="the message you want to sign") sub.add_argument("--pem", required=True, type=str, help="the path to a validator pem file") sub.add_argument( - "--validator-index", + "--index", required=False, type=int, default=0, @@ -91,7 +91,7 @@ def create_new_wallet(args: Any): def sign_message(args: Any): path = Path(args.pem).expanduser().resolve() - validator_signer = ValidatorSigner.from_pem_file(path, args.validator_index) + validator_signer = ValidatorSigner.from_pem_file(path, args.index) signed_message = sign_message_by_validator(args.message, validator_signer) utils.dump_out_json(signed_message.to_dictionary()) From 0843d4f17848907d4e766d3407c6b20833cf0704 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 13 Feb 2025 16:46:24 +0200 Subject: [PATCH 44/84] fix --- multiversx_sdk_cli/transactions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 597f7c29..7c18e4b9 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -133,7 +133,7 @@ def transaction_event_to_dictionary(event: TransactionEvent) -> dict[str, Any]: "identifier": event.identifier, "topics": [topic.hex() for topic in event.topics], "data": event.data.decode(), - "additional_data": [data.decode() for data in event.additional_data], + "additional_data": [data.hex() for data in event.additional_data], } From 15ccd39e16a762948562aae2299288b5ee9918c2 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 13 Feb 2025 16:51:45 +0200 Subject: [PATCH 45/84] run tests sequentially to prevent occasional nonce collisions --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bba1e891..7f31f431 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ jobs: matrix: os: [ubuntu-latest, macos-latest] python-version: [3.11] + max-parallel: 1 # This ensures jobs run sequentially, not concurrently steps: - uses: actions/checkout@v2 From 3a9bd2323d2acb1f6438d190d421442389b84635 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 19 Feb 2025 11:49:58 +0200 Subject: [PATCH 46/84] drop get_account and get_transaction --- multiversx_sdk_cli/cli.py | 2 - multiversx_sdk_cli/cli_accounts.py | 85 -------------------------- multiversx_sdk_cli/cli_transactions.py | 25 -------- multiversx_sdk_cli/tests/test_proxy.py | 28 --------- multiversx_sdk_cli/transactions.py | 14 ++--- 5 files changed, 7 insertions(+), 147 deletions(-) delete mode 100644 multiversx_sdk_cli/cli_accounts.py diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 0970ff97..3804bad7 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -9,7 +9,6 @@ from multiversx_sdk import LibraryConfig from rich.logging import RichHandler -import multiversx_sdk_cli.cli_accounts import multiversx_sdk_cli.cli_config import multiversx_sdk_cli.cli_contracts import multiversx_sdk_cli.cli_data @@ -110,7 +109,6 @@ def setup_parser(args: List[str]): commands.append(multiversx_sdk_cli.cli_contracts.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_transactions.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_validators.setup_parser(args, subparsers)) - commands.append(multiversx_sdk_cli.cli_accounts.setup_parser(subparsers)) commands.append(multiversx_sdk_cli.cli_ledger.setup_parser(subparsers)) commands.append(multiversx_sdk_cli.cli_wallet.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_validator_wallet.setup_parser(args, subparsers)) diff --git a/multiversx_sdk_cli/cli_accounts.py b/multiversx_sdk_cli/cli_accounts.py deleted file mode 100644 index 1877fc39..00000000 --- a/multiversx_sdk_cli/cli_accounts.py +++ /dev/null @@ -1,85 +0,0 @@ -import logging -from typing import Any - -from multiversx_sdk import ( - AccountOnNetwork, - Address, - BlockCoordinates, - ProxyNetworkProvider, -) - -from multiversx_sdk_cli import cli_shared, utils -from multiversx_sdk_cli.config import get_config_for_network_providers - -logger = logging.getLogger("cli.accounts") - - -def setup_parser(subparsers: Any) -> Any: - parser = cli_shared.add_group_subparser(subparsers, "account", "Get Account data (nonce, balance) from the Network") - subparsers = parser.add_subparsers() - - sub = cli_shared.add_command_subparser(subparsers, "account", "get", "Query account details (nonce, balance etc.)") - cli_shared.add_proxy_arg(sub) - _add_address_arg(sub) - mutex = sub.add_mutually_exclusive_group() - mutex.add_argument("--balance", action="store_true", help="whether to only fetch the balance") - mutex.add_argument("--nonce", action="store_true", help="whether to only fetch the nonce") - mutex.add_argument("--username", action="store_true", help="whether to only fetch the username") - cli_shared.add_omit_fields_arg(sub) - sub.set_defaults(func=get_account) - - parser.epilog = cli_shared.build_group_epilog(subparsers) - return subparsers - - -def _add_address_arg(sub: Any): - sub.add_argument("--address", required=True, help="🖄 the address to query") - - -def get_account(args: Any): - omitted_fields = cli_shared.parse_omit_fields_arg(args) - - proxy_url = args.proxy - address = args.address - config = get_config_for_network_providers() - proxy = ProxyNetworkProvider(url=proxy_url, config=config) - account = proxy.get_account(Address.new_from_bech32(address)) - - if args.balance: - print(account.balance) - elif args.nonce: - print(account.nonce) - elif args.username: - print(account.username) - else: - account = _account_on_network_to_dictionary(account) - utils.dump_out_json(utils.omit_fields(account, omitted_fields)) - - -def _account_on_network_to_dictionary(account: AccountOnNetwork) -> dict[str, Any]: - return { - "address": account.address.to_bech32(), - "nonce": account.nonce, - "balance": account.balance, - "is_guarded": account.is_guarded, - "username": account.username, - "block_coordinates": ( - _block_coordinates_to_dictionary(account.block_coordinates) if account.block_coordinates else {} - ), - "contract_code_hash": account.contract_code_hash.hex(), - "contract_code": account.contract_code.hex(), - "contract_developer_reward": account.contract_developer_reward, - "contract_owner_address": account.contract_owner_address.to_bech32() if account.contract_owner_address else "", - "is_contract_upgradable": account.is_contract_upgradable, - "is_contract_readable": account.is_contract_readable, - "is_contract_payable": account.is_contract_payable, - "is_contract_payable_by_contract": account.is_contract_payable_by_contract, - } - - -def _block_coordinates_to_dictionary(block: BlockCoordinates) -> dict[str, Any]: - return { - "nonce": block.nonce, - "hash": block.hash.hex(), - "root_hash": block.root_hash.hex(), - } diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 399cfe35..02b79e2b 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -66,17 +66,6 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) sub.set_defaults(func=send_transaction) - sub = cli_shared.add_command_subparser( - subparsers, - "tx", - "get", - f"Get a transaction.{CLIOutputBuilder.describe(with_emitted=False, with_transaction_on_network=True)}", - ) - sub.add_argument("--hash", required=True, help="the hash") - cli_shared.add_proxy_arg(sub) - cli_shared.add_omit_fields_arg(sub) - sub.set_defaults(func=get_transaction) - sub = cli_shared.add_command_subparser( subparsers, "tx", @@ -196,20 +185,6 @@ def send_transaction(args: Any): utils.dump_out_json(output, outfile=args.outfile) -def get_transaction(args: Any): - if not args.proxy: - raise BadUsage("Proxy argument not provided") - - omit_fields = cli_shared.parse_omit_fields_arg(args) - - config = get_config_for_network_providers() - proxy = ProxyNetworkProvider(url=args.proxy, config=config) - - transaction = proxy.get_transaction(args.hash) - output = CLIOutputBuilder().set_transaction_on_network(transaction, omit_fields).build() - utils.dump_out_json(output) - - def sign_transaction(args: Any): cli_shared.check_broadcast_args(args) diff --git a/multiversx_sdk_cli/tests/test_proxy.py b/multiversx_sdk_cli/tests/test_proxy.py index 45efb5e7..6b49e889 100644 --- a/multiversx_sdk_cli/tests/test_proxy.py +++ b/multiversx_sdk_cli/tests/test_proxy.py @@ -4,20 +4,6 @@ from multiversx_sdk_cli.config import get_config_for_network_providers -def test_get_account(): - result = main( - [ - "account", - "get", - "--address", - "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", - "--proxy", - "https://testnet-api.multiversx.com", - ] - ) - assert False if result else True - - def test_sync_nonce(): account = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") config = get_config_for_network_providers() @@ -39,17 +25,3 @@ def test_query_contract(): ] ) assert False if result else True - - -def test_get_transaction(): - result = main( - [ - "tx", - "get", - "--proxy", - "https://devnet-api.multiversx.com", - "--hash", - "06f381ee88ed27ba08a35f995f17dceb737e1a99c5c4da0c247bbe7aa1d18551", - ] - ) - assert False if result else True diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 7c18e4b9..df30cdd0 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -162,19 +162,19 @@ def transaction_on_network_to_dictionary(tx: TransactionOnNetwork) -> dict[str, "round": tx.round, "epoch": tx.epoch, "timestamp": tx.timestamp, - "block_hash": tx.block_hash.hex(), - "miniblock_hash": tx.miniblock_hash.hex(), - "sender_shard": tx.sender_shard, - "receiver_shard": tx.receiver_shard, + "blockHash": tx.block_hash.hex(), + "miniBlockHash": tx.miniblock_hash.hex(), + "senderShard": tx.sender_shard, + "receiverShard": tx.receiver_shard, "value": tx.value, - "gas_limit": tx.gas_limit, - "gas_price": tx.gas_price, + "gasLimit": tx.gas_limit, + "gasPrice": tx.gas_price, "function": tx.function, "data": tx.data.decode(), "version": tx.version, "options": tx.options, "signature": tx.signature.hex(), "status": tx.status.status, - "smart_contract_results": [smart_contract_result_to_dictionary(res) for res in tx.smart_contract_results], + "smartContractResults": [smart_contract_result_to_dictionary(res) for res in tx.smart_contract_results], "logs": transaction_logs_to_dictionary(tx.logs), } From 2312490970345c98b55e9ecbdf00e3b6bde1f651 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 19 Feb 2025 14:41:05 +0200 Subject: [PATCH 47/84] use native auth client from sdk-py --- multiversx_sdk_cli/cli_faucet.py | 18 +-- multiversx_sdk_cli/errors.py | 5 - multiversx_sdk_cli/native_auth_client.py | 104 ------------ .../tests/test_native_auth_client.py | 151 ------------------ requirements.txt | 3 +- 5 files changed, 10 insertions(+), 271 deletions(-) delete mode 100644 multiversx_sdk_cli/native_auth_client.py delete mode 100644 multiversx_sdk_cli/tests/test_native_auth_client.py diff --git a/multiversx_sdk_cli/cli_faucet.py b/multiversx_sdk_cli/cli_faucet.py index 19113ace..a9700528 100644 --- a/multiversx_sdk_cli/cli_faucet.py +++ b/multiversx_sdk_cli/cli_faucet.py @@ -1,14 +1,12 @@ import logging import webbrowser from enum import Enum -from typing import Any, List, Tuple +from typing import Any + +from multiversx_sdk import Message, NativeAuthClient, NativeAuthClientConfig from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.errors import BadUserInput -from multiversx_sdk_cli.native_auth_client import ( - NativeAuthClient, - NativeAuthClientConfig, -) logger = logging.getLogger("cli.faucet") @@ -23,13 +21,13 @@ class ApiUrls(Enum): TESTNET = "https://testnet-api.multiversx.com" -def setup_parser(args: List[str], subparsers: Any) -> Any: +def setup_parser(args: list[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser(subparsers, "faucet", "Get xEGLD on Devnet or Testnet") subparsers = parser.add_subparsers() sub = cli_shared.add_command_subparser(subparsers, "faucet", "request", "Request xEGLD.") cli_shared.add_wallet_args(args, sub) - sub.add_argument("--chain", required=True, help="the chain identifier") + sub.add_argument("--chain", required=True, choices=["D", "T"], help="the chain identifier") sub.set_defaults(func=faucet) parser.epilog = cli_shared.build_group_epilog(subparsers) @@ -45,9 +43,9 @@ def faucet(args: Any): init_token = client.initialize() token_for_siginig = f"{account.address.to_bech32()}{init_token}" - signature = account.sign_message(token_for_siginig.encode()) + signature = account.sign_message(Message(token_for_siginig.encode())) - access_token = client.get_token(address=account.address.to_bech32(), token=init_token, signature=signature) + access_token = client.get_token(address=account.address, token=init_token, signature=signature.hex()) logger.info(f"Requesting funds for address: {account.address.to_bech32()}") call_web_wallet_faucet(wallet_url=wallet, access_token=access_token) @@ -58,7 +56,7 @@ def call_web_wallet_faucet(wallet_url: str, access_token: str): webbrowser.open_new_tab(faucet_url) -def get_wallet_and_api_urls(args: Any) -> Tuple[str, str]: +def get_wallet_and_api_urls(args: Any) -> tuple[str, str]: chain: str = args.chain if chain.upper() == "D": diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 2ef320b6..4a8ff409 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -177,11 +177,6 @@ def __init__(self, message: str, inner: Any = None): super().__init__(message, str(inner)) -class NativeAuthClientError(KnownError): - def __init__(self, message: str): - super().__init__(message) - - class IncorrectWalletError(KnownError): def __init__(self, message: str): super().__init__(message) diff --git a/multiversx_sdk_cli/native_auth_client.py b/multiversx_sdk_cli/native_auth_client.py deleted file mode 100644 index 639c97d5..00000000 --- a/multiversx_sdk_cli/native_auth_client.py +++ /dev/null @@ -1,104 +0,0 @@ -import base64 -import json -from typing import Any, Dict, Optional - -import requests - -from multiversx_sdk_cli.errors import NativeAuthClientError - -DEFAULT_EXPIRY_TIME_IN_SECONDS = 60 * 60 * 2 -DEFAULT_API_URL = "https://api.multiversx.com" - - -class NativeAuthClientConfig: - def __init__( - self, - origin: str = "", - api_url: str = DEFAULT_API_URL, - expiry_seconds: int = DEFAULT_EXPIRY_TIME_IN_SECONDS, - block_hash_shard: Optional[int] = None, - gateway_url: Optional[str] = None, - extra_request_headers: Optional[Dict[str, str]] = None, - ) -> None: - self.origin = origin - self.api_url = api_url - self.expiry_seconds = expiry_seconds - self.block_hash_shard = block_hash_shard - self.gateway_url = gateway_url - self.extra_request_headers = extra_request_headers - - -class NativeAuthClient: - def __init__(self, config: Optional[NativeAuthClientConfig] = None) -> None: - self.config = config or NativeAuthClientConfig() - - def initialize(self, extra_info: Dict[Any, Any] = {}) -> str: - block_hash = self.get_current_block_hash() - encoded_extra_info = self._encode_value(json.dumps(extra_info)) - encoded_origin = self._encode_value(self.config.origin) - - return f"{encoded_origin}.{block_hash}.{self.config.expiry_seconds}.{encoded_extra_info}" - - def get_token(self, address: str, token: str, signature: str) -> str: - encoded_address = self._encode_value(address) - encoded_token = self._encode_value(token) - - return f"{encoded_address}.{encoded_token}.{signature}" - - def get_current_block_hash(self) -> str: - if self.config.gateway_url: - return self._get_current_block_hash_using_gateway() - return self._get_current_block_hash_using_api() - - def _get_current_block_hash_using_gateway(self) -> str: - round = self._get_current_round() - url = f"{self.config.gateway_url}/blocks/by-round/{round}" - response = self._execute_request(url) - blocks: list[dict[str, Any]] = response["data"]["blocks"] - block: dict[str, str] = [b for b in blocks if b["shard"] == self.config.block_hash_shard][0] - return block["hash"] - - def _get_current_round(self) -> int: - if self.config.gateway_url is None: - raise NativeAuthClientError("Gateway URL not set") - - if self.config.block_hash_shard is None: - raise NativeAuthClientError("Blockhash shard not set") - - url = f"{self.config.gateway_url}/network/status/{self.config.block_hash_shard}" - response = self._execute_request(url) - status: dict[str, int] = response["data"]["status"] - - return status["erd_current_round"] - - def _get_current_block_hash_using_api(self) -> str: - try: - url = f"{self.config.api_url}/blocks/latest?ttl={self.config.expiry_seconds}&fields=hash" - response: dict[str, str] = self._execute_request(url) - if response["hash"]: - return response["hash"] - except Exception: - pass - - return self._get_current_block_hash_using_api_fallback() - - def _get_current_block_hash_using_api_fallback(self) -> str: - url = f"{self.config.api_url}/blocks?size=1&fields=hash" - - if self.config.block_hash_shard: - url += f"&shard={self.config.block_hash_shard}" - - response: list[dict[str, str]] = self._execute_request(url) - return response[0]["hash"] - - def _encode_value(self, string: str) -> str: - encoded = base64.b64encode(string.encode("utf-8")).decode("utf-8") - return self._escape(encoded) - - def _escape(self, string: str) -> str: - return string.replace("+", "-").replace("/", "_").replace("=", "") - - def _execute_request(self, url: str) -> Any: - response = requests.get(url=url, headers=self.config.extra_request_headers) - response.raise_for_status() - return response.json() diff --git a/multiversx_sdk_cli/tests/test_native_auth_client.py b/multiversx_sdk_cli/tests/test_native_auth_client.py deleted file mode 100644 index 93800cd8..00000000 --- a/multiversx_sdk_cli/tests/test_native_auth_client.py +++ /dev/null @@ -1,151 +0,0 @@ -from typing import Any, List - -import pytest - -from multiversx_sdk_cli.native_auth_client import ( - NativeAuthClient, - NativeAuthClientConfig, -) - - -def mock(mocker: Any, code: int, response: Any): - mock_response = mocker.Mock() - mock_response.status_code = code - mock_response.json.return_value = response - mocker.patch("requests.get", return_value=mock_response) - - -def mock_side_effect(mocker: Any, responses: List[Any]): - def side_effect(*args: Any, **kwargs: Any): - response = responses.pop(0) - mock_response = mocker.Mock() - mock_response.status_code = response["code"] - mock_response.json.return_value = response["response"] - return mock_response - - mocker.patch("requests.get", side_effect=side_effect) - - -class TestNativeAuth: - ADDRESS = "erd1qnk2vmuqywfqtdnkmauvpm8ls0xh00k8xeupuaf6cm6cd4rx89qqz0ppgl" - SIGNATURE = "906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d" - BLOCK_HASH = "ab459013b27fdc6fe98eed567bd0c1754e0628a4cc16883bf0170a29da37ad46" - TTL = 86400 - ORIGIN = "https://api.multiversx.com" - TOKEN = f"aHR0cHM6Ly9hcGkubXVsdGl2ZXJzeC5jb20.{BLOCK_HASH}.{TTL}.e30" - ACCESS_TOKEN = "ZXJkMXFuazJ2bXVxeXdmcXRkbmttYXV2cG04bHMweGgwMGs4eGV1cHVhZjZjbTZjZDRyeDg5cXF6MHBwZ2w.YUhSMGNITTZMeTloY0drdWJYVnNkR2wyWlhKemVDNWpiMjAuYWI0NTkwMTNiMjdmZGM2ZmU5OGVlZDU2N2JkMGMxNzU0ZTA2MjhhNGNjMTY4ODNiZjAxNzBhMjlkYTM3YWQ0Ni44NjQwMC5lMzA.906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d" - INVALID_HASH_ERROR = "Validation failed for block hash 'hash'. Length should be 64." - - def test_latest_block_should_return_signable_token(self, mocker: Any): - mock(mocker, 200, [{"hash": self.BLOCK_HASH}]) - config = NativeAuthClientConfig(origin=self.ORIGIN, expiry_seconds=self.TTL) - client = NativeAuthClient(config) - token = client.initialize() - assert token == self.TOKEN - - def test_throws_internal_server_error(self, mocker: Any): - mock(mocker, 500, {}) - client = NativeAuthClient() - with pytest.raises(Exception): - client.initialize() - - # if `/blocks/latest` raises error should fallback to `/blocks?size=1` - def test_fallback_mechanism(self, mocker: Any): - mock( - mocker, - 400, - [ - { - "statusCode": 400, - "message": self.INVALID_HASH_ERROR, - "error": "Bad request", - } - ], - ) - mock(mocker, 200, {"hash": self.BLOCK_HASH}) - - config = NativeAuthClientConfig(origin=self.ORIGIN, expiry_seconds=self.TTL) - client = NativeAuthClient(config) - - token = client.initialize() - assert token == self.TOKEN - - def test_generate_access_token(self): - client = NativeAuthClient() - access_token = client.get_token(self.ADDRESS, self.TOKEN, self.SIGNATURE) - assert access_token == self.ACCESS_TOKEN - - -class TestNativeAuthWithGateway: - ADDRESS = "erd1qnk2vmuqywfqtdnkmauvpm8ls0xh00k8xeupuaf6cm6cd4rx89qqz0ppgl" - SIGNATURE = "906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d" - BLOCK_HASH = "ab459013b27fdc6fe98eed567bd0c1754e0628a4cc16883bf0170a29da37ad46" - TTL = 86400 - ORIGIN = "https://api.multiversx.com" - TOKEN = f"aHR0cHM6Ly9hcGkubXVsdGl2ZXJzeC5jb20.{BLOCK_HASH}.{TTL}.e30" - ACCESS_TOKEN = "ZXJkMXFuazJ2bXVxeXdmcXRkbmttYXV2cG04bHMweGgwMGs4eGV1cHVhZjZjbTZjZDRyeDg5cXF6MHBwZ2w.YUhSMGNITTZMeTloY0drdWJYVnNkR2wyWlhKemVDNWpiMjAuYWI0NTkwMTNiMjdmZGM2ZmU5OGVlZDU2N2JkMGMxNzU0ZTA2MjhhNGNjMTY4ODNiZjAxNzBhMjlkYTM3YWQ0Ni44NjQwMC5lMzA.906e79d54e69e688680abee54ec0c49ce2561eb5abfd01865b31cb3ed738272c7cfc4fc8cc1c3590dd5757e622639b01a510945d7f7c9d1ceda20a50a817080d" - LATEST_ROUND = 115656 - METASHARD = 4294967295 - GATEWAY = "https://gateway.multiversx.com" - - def test_latest_block_should_return_signable_token(self, mocker: Any): - responses = [ - { - "code": 200, - "response": {"data": {"status": {"erd_current_round": self.LATEST_ROUND}}}, - }, - { - "code": 200, - "response": {"data": {"blocks": [{"shard": self.METASHARD, "hash": self.BLOCK_HASH}]}}, - }, - ] - mock_side_effect(mocker, responses) - - config = NativeAuthClientConfig( - origin=self.ORIGIN, - gateway_url=self.GATEWAY, - block_hash_shard=self.METASHARD, - expiry_seconds=self.TTL, - ) - client = NativeAuthClient(config) - token = client.initialize() - assert token == self.TOKEN - - def test_should_raise_internal_server_error(self, mocker: Any): - responses = [ - { - "code": 500, - "response": {"data": {"status": {"erd_current_round": self.LATEST_ROUND}}}, - } - ] - mock_side_effect(mocker, responses) - - config = NativeAuthClientConfig(gateway_url=self.GATEWAY, block_hash_shard=self.METASHARD) - client = NativeAuthClient(config) - - with pytest.raises(Exception): - client.initialize() - - def test_raises_internal_server_error_on_second_request(self, mocker: Any): - responses = [ - { - "code": 200, - "response": {"data": {"status": {"erd_current_round": self.LATEST_ROUND}}}, - }, - {"code": 500, "response": {""}}, - ] - mock_side_effect(mocker, responses) - - config = NativeAuthClientConfig(gateway_url=self.GATEWAY, block_hash_shard=self.METASHARD) - client = NativeAuthClient(config) - - with pytest.raises(Exception): - client.initialize() - - def test_generate_access_token(self): - config = NativeAuthClientConfig(gateway_url=self.GATEWAY, block_hash_shard=self.METASHARD) - client = NativeAuthClient(config) - - access_token = client.get_token(address=self.ADDRESS, token=self.TOKEN, signature=self.SIGNATURE) - - assert access_token == self.ACCESS_TOKEN diff --git a/requirements.txt b/requirements.txt index bd3ddbe8..540ea6d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,5 @@ ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk[ledger]==1.1.0 +# multiversx-sdk[ledger]==1.1.0 +git+https://github.com/multiversx/mx-sdk-py@feat/next#egg=multiversx_sdk From fe13eb685ce5168f284b02239563c6d1f6532c5a Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 19 Feb 2025 16:20:37 +0200 Subject: [PATCH 48/84] removed deprecated installation script --- mxpy-up.py | 387 ----------------------------------------------------- 1 file changed, 387 deletions(-) delete mode 100644 mxpy-up.py diff --git a/mxpy-up.py b/mxpy-up.py deleted file mode 100644 index 24177d3a..00000000 --- a/mxpy-up.py +++ /dev/null @@ -1,387 +0,0 @@ -import logging -import os -import os.path -import shutil -import stat -import subprocess -import sys -from argparse import ArgumentParser -from pathlib import Path -from typing import List, Tuple - -logger = logging.getLogger("installer") - -MIN_REQUIRED_PYTHON_VERSION = (3, 8, 0) -sdk_path = Path("~/multiversx-sdk").expanduser().resolve() - - -def main(): - parser = ArgumentParser() - parser.add_argument("--exact-version", help="the exact version of mxpy to install") - parser.add_argument("--from-branch", help="use a branch of multiversx/mx-sdk-py-cli") - parser.add_argument("--not-interactive", action="store_true", default=False) - parser.add_argument("--verbose", action="store_true", default=False) - parser.add_argument("--ignore-deprecation", action="store_true", default=False, help="'mxpy-up.py' is obsolete, install using 'pipx': https://docs.multiversx.com/sdk-and-tools/sdk-py/installing-mxpy/#install-using-pipx") - parser.set_defaults(modify_path=True) - args = parser.parse_args() - - logger.warning("'mxpy-up.py' is deprecated. Check out the documentation on how to install using `pipx`: https://docs.multiversx.com/sdk-and-tools/sdk-py/installing-mxpy/#install-using-pipx.") - - if not args.ignore_deprecation: - raise Exception("'mxpy-up.py' is deprecated, please install using `pipx`: https://docs.multiversx.com/sdk-and-tools/sdk-py/installing-mxpy/#install-using-pipx. If installing using 'mxpy-up` is necessary, provide the `--ignore-deprecation` flag.") - - exact_version = args.exact_version - from_branch = args.from_branch - interactive = not args.not_interactive - verbose = args.verbose - - log_level = logging.DEBUG if verbose else logging.INFO - logging.basicConfig(level=log_level) - - if get_operating_system() == "windows": - print(""" -IMPORTANT NOTE -============== - -Windows support is limited and experimental. -""") - confirm_continuation(interactive) - - guard_non_root_user() - guard_python_version() - migrate_v6(interactive) - - # In case of a fresh install: - sdk_path.mkdir(parents=True, exist_ok=True) - create_venv() - logger.info("Installing the necessary dependencies...") - install_mxpy(exact_version, from_branch, verbose) - - run_post_install_checks() - - if interactive: - guide_system_path_integration() - - logger.warning("Installing `mxpy` using `mxpy-up.py` is deprecated. Check out the documentation on how to install using `pipx`: https://docs.multiversx.com/sdk-and-tools/sdk-py/installing-mxpy/#install-using-pipx") - - -def guard_non_root_user(): - logger.debug("Checking user (should not be root).") - - operating_system = get_operating_system() - - if operating_system == "windows": - return - if os.getuid() == 0: - raise InstallError("You should not install mxpy as root.") - - -def guard_python_version(): - python_version = (sys.version_info.major, sys.version_info.minor, sys.version_info.micro) - - logger.debug("Checking Python version.") - logger.debug(f"Python version: {format_version(python_version)}") - if python_version < MIN_REQUIRED_PYTHON_VERSION: - raise InstallError(f"You need Python {format_version(MIN_REQUIRED_PYTHON_VERSION)} or later.") - - -def format_version(version: Tuple[int, int, int]) -> str: - major, minor, patch = version - return f"{major}.{minor}.{patch}" - - -def get_operating_system(): - aliases = { - "linux": "linux", - "linux1": "linux", - "linux2": "linux", - "darwin": "osx", - "win32": "windows", - "cygwin": "windows", - "msys": "windows" - } - - operating_system = aliases.get(sys.platform) - if operating_system is None: - raise InstallError(f"Unknown platform: {sys.platform}") - - return operating_system - - -def migrate_v6(interactive: bool): - nodejs_folder = sdk_path / "nodejs" - - if nodejs_folder.exists(): - print(f""" -In previous versions of the SDK, the "wasm-opt" tool was installed in the "nodejs" folder. - -This is no longer the case - now, "wasm-opt" is a separate module. - -The following folder will be removed: {nodejs_folder}. - -You may need to reinstall wasm-opt using `mxpy deps install wasm-opt`. -""") - confirm_continuation(interactive) - - shutil.rmtree(nodejs_folder) - - global_testnet_toml = sdk_path / "testnet.toml" - if global_testnet_toml.exists(): - global_testnet_toml.unlink() - - -def create_venv(): - require_python_venv_tools() - venv_folder = get_mxpy_venv_path() - venv_folder.mkdir(parents=True, exist_ok=True) - - logger.debug(f"Creating virtual environment in: {venv_folder}.") - import venv - builder = venv.EnvBuilder(with_pip=True, symlinks=True) - builder.clear_directory(venv_folder) - builder.create(venv_folder) - - logger.debug(f"Virtual environment has been created in: {venv_folder}.") - - -def require_python_venv_tools(): - operating_system = get_operating_system() - - try: - import ensurepip - import venv - logger.debug(f"Packages found: {ensurepip}, {venv}.") - except ModuleNotFoundError: - if operating_system == "linux": - python_venv = f"python{sys.version_info.major}.{sys.version_info.minor}-venv" - raise InstallError(f'Packages [venv] or [ensurepip] not found. Please run "sudo apt install {python_venv}" and then run mxpy-up again.') - else: - raise InstallError("Packages [venv] or [ensurepip] not found, please install them first. See https://docs.python.org/3/tutorial/venv.html.") - - -def get_mxpy_venv_path(): - return sdk_path / "mxpy-venv" - - -def install_mxpy(exact_version: str, from_branch: str, verbose: bool): - logger.debug("Installing mxpy in virtual environment...") - - if from_branch: - package_to_install = f"https://github.com/multiversx/mx-sdk-py-cli/archive/refs/heads/{from_branch}.zip" - else: - package_to_install = "multiversx_sdk_cli" if not exact_version else f"multiversx_sdk_cli=={exact_version}" - - venv_path = get_mxpy_venv_path() - - return_code = run_in_venv(["python3", "-m", "pip", "install", "--upgrade", "pip"], venv_path, verbose) - if return_code != 0: - raise InstallError("Could not upgrade pip.") - return_code = run_in_venv(["pip3", "install", "--no-cache-dir", package_to_install], venv_path, verbose) - if return_code != 0: - raise InstallError("Could not install mxpy.") - - logger.debug("Creating mxpy shortcut...") - - shortcut_path = sdk_path / "mxpy" - - try: - shortcut_path.unlink() - logger.debug(f"Removed existing shortcut: {shortcut_path}") - except FileNotFoundError: - logger.debug(f"Shortcut does not exist yet: {shortcut_path}") - pass - - shortcut_content = get_mxpy_shortcut_content() - shortcut_path.write_text(shortcut_content) - - st = os.stat(shortcut_path) - os.chmod(shortcut_path, st.st_mode | stat.S_IEXEC) - - logger.info("You have successfully installed mxpy.") - - -def get_mxpy_shortcut_content(): - operating_system = get_operating_system() - venv_path = get_mxpy_venv_path() - - if operating_system == "windows": - return f"""#!/bin/sh -. "{venv_path / 'Scripts' / 'activate'}" && python3 -m multiversx_sdk_cli.cli "$@" && deactivate -""" - - return f"""#!/bin/sh -. "{venv_path / 'bin' / 'activate'}" && python3 -m multiversx_sdk_cli.cli "$@" && deactivate -""" - - -def run_in_venv(args: List[str], venv_path: Path, verbose: bool): - env = os.environ.copy() - - if "PYTHONHOME" in env: - del env["PYTHONHOME"] - - env["PATH"] = str(venv_path / "bin") + ":" + env["PATH"] - env["VIRTUAL_ENV"] = str(venv_path) - - if verbose: - process = subprocess.Popen(args, env=env) - else: - process = subprocess.Popen(args, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) - - return process.wait() - - -def run_post_install_checks(): - multiversx_sdk_path = Path("~/multiversx-sdk").expanduser() - - logger.debug("Running post-install checks...") - - if multiversx_sdk_path.exists(): - logger.debug("~/multiversx-sdk exists OK") - else: - logger.warning("~/multiversx-sdk exists NOK") - - if (multiversx_sdk_path / "mxpy").exists(): - logger.debug("~/multiversx-sdk/mxpy shortcut created OK") - else: - logger.warning("~/multiversx-sdk/mxpy shortcut created NOK") - - -def guide_system_path_integration(): - interactive = True - operating_system = get_operating_system() - - if operating_system == "windows": - print(f""" -############################################################################### -On Windows, for the "mxpy" command shortcut to be available, you MUST ADD the directory "{sdk_path}" to the system PATH. - -You can do this by following these steps: - -https://superuser.com/questions/949560/how-do-i-set-system-environment-variables-in-windows-10 - -############################################################################### -Do you UNDERSTAND the above? -############################################################################### -""") - confirm_continuation(interactive) - return - - old_export_directive = f'export PATH="{Path("~/elrondsdk").expanduser()}:$PATH"\t# elrond-sdk' - new_export_directive = 'export PATH="${{HOME}}/multiversx-sdk:$PATH"\t# multiversx-sdk' - - profile_files = get_profile_files() - - if not profile_files: - print(f""" -############################################################################### -No shell profile files have been found. - -The "mxpy" command shortcut will not be available until you add the directory "{sdk_path}" to the system PATH. -############################################################################### -Do you UNDERSTAND the above? -""") - confirm_continuation(interactive) - return - - profile_files_formatted = "\n".join(f" - {file}" for file in profile_files) - profile_files_contents = [profile_file.read_text() for profile_file in profile_files] - any_old_export_directive = any(old_export_directive in content for content in profile_files_contents) - any_new_export_directive = any(new_export_directive in content for content in profile_files_contents) - - if any_old_export_directive: - # We don't perform the removal automatically (a bit risky) - print(f""" -############################################################################### -It seems that the old path "~/elrondsdk" is still configured in shell profile. - -Please MANUALLY remove it from the shell profile (now or after the installer script ends). - -Your shell profile files: -{profile_files_formatted} - -The entry (entries) to remove: - {old_export_directive} -############################################################################### -Make sure you UNDERSTAND the above before proceeding further. -############################################################################### -""") - confirm_continuation(interactive) - - if any_new_export_directive: - # Note: in some (rare) cases, here we'll have false positives (e.g. if the export directive is commented out). - print(f""" -############################################################################### -It seems that the path "~/multiversx-sdk" is already configured in shell profile. - -To confirm this, CHECK the shell profile (now or after the installer script ends). - -Your shell profile files: -{profile_files_formatted} - -The entry to check (it should be present): - {new_export_directive}. -############################################################################### -Make sure you UNDERSTAND the above before proceeding further. -############################################################################### -""") - confirm_continuation(interactive) - return - - print(f""" -############################################################################### -In order to use the "mxpy" command shortcut, you have to manually extend the PATH variable to include "~/multiversx-sdk". - -In order to manually extend the PATH variable, add the following line to your shell profile file upon installation: - - export PATH="${{HOME}}/multiversx-sdk:${{PATH}}" - -Your shell profile files: -{profile_files_formatted} - -Upon editing the shell profile file, you may have to RESTART THE USER SESSION for the changes to take effect. -""") - confirm_continuation(interactive) - - -def get_profile_files() -> List[Path]: - files = [ - Path("~/.profile").expanduser().resolve(), - Path("~/.bashrc").expanduser().resolve(), - Path("~/.bash_profile").expanduser().resolve(), - Path("~/.zshrc").expanduser().resolve() - ] - - return [file for file in files if file.exists()] - - -class InstallError(Exception): - def __init__(self, message: str): - super().__init__(message) - - -def confirm_continuation(interactive: bool): - if not interactive: - return - - answer = input("Continue? (y/n)") - if answer.lower() not in ["y", "yes"]: - print("Confirmation not given. Will stop.") - exit(1) - - -if __name__ == "__main__": - try: - main() - except Exception as err: - logger.fatal(err) - sys.exit(1) - - print(""" -############################################################################### -Installer script finished successfully. -############################################################################### -For more information go to https://docs.multiversx.com. -For support, please contact us at http://discord.gg/MultiversXBuilders. -############################################################################### -""") From 9f14e8461373ae2c9f72703ace2f8e7f873cbecb Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 20 Feb 2025 16:48:08 +0200 Subject: [PATCH 49/84] make args non-required and perform args validation on command --- multiversx_sdk_cli/args_validation.py | 69 ++++++++++++++ multiversx_sdk_cli/cli_contracts.py | 47 +++++++--- multiversx_sdk_cli/cli_delegation.py | 6 +- multiversx_sdk_cli/cli_shared.py | 89 ++++++++----------- multiversx_sdk_cli/cli_transactions.py | 38 ++++---- multiversx_sdk_cli/cli_validators.py | 13 ++- multiversx_sdk_cli/cli_wallet.py | 2 +- .../delegation/staking_provider.py | 14 +-- multiversx_sdk_cli/dns.py | 16 +++- multiversx_sdk_cli/errors.py | 5 ++ multiversx_sdk_cli/tests/test_cli_shared.py | 50 ----------- 11 files changed, 198 insertions(+), 151 deletions(-) create mode 100644 multiversx_sdk_cli/args_validation.py delete mode 100644 multiversx_sdk_cli/tests/test_cli_shared.py diff --git a/multiversx_sdk_cli/args_validation.py b/multiversx_sdk_cli/args_validation.py new file mode 100644 index 00000000..56072e70 --- /dev/null +++ b/multiversx_sdk_cli/args_validation.py @@ -0,0 +1,69 @@ +from typing import Any + +from multiversx_sdk_cli.errors import InvalidArgumentsError + + +def ensure_required_transaction_args_are_provided(args: Any): + _ensure_nonce_args(args) + _ensure_receiver_args(args) + _ensure_gas_limit_args(args) + + +def _ensure_nonce_args(args: Any): + """If nonce is not provided, ensure that recall_nonce is provided. If recall_nonce is provided, ensure that proxy is provided.""" + if hasattr(args, "nonce") and args.nonce is None: + if not args.recall_nonce: + raise InvalidArgumentsError("Either --nonce or --recall-nonce must be provided") + + if hasattr(args, "proxy") and not args.proxy: + raise InvalidArgumentsError("--proxy must be provided if --recall-nonce is used") + + +def _ensure_receiver_args(args: Any): + """Ensure that receiver is provided.""" + if hasattr(args, "receiver") and not args.receiver: + raise InvalidArgumentsError("--receiver must be provided") + + +def _ensure_gas_limit_args(args: Any): + """Ensure that gas_limit is provided.""" + if hasattr(args, "gas_limit") and not args.gas_limit: + if hasattr(args, "estimate_gas") and not args.estimate_gas: + raise InvalidArgumentsError("If --gas-limit not provided, --estimate-gas must be provided") + + +def ensure_wallet_args_are_provided(args: Any): + signing_methods = [args.pem, args.keyfile, args.ledger] + + if all(signing_methods): + raise InvalidArgumentsError("Only one of --pem, --keyfile, or --ledger must be provided") + + if not any(signing_methods): + raise InvalidArgumentsError("One of --pem, --keyfile, or --ledger must be provided") + + +def ensure_relayer_wallet_args_are_provided(args: Any): + signing_methods = [args.relayer_pem, args.relayer_keyfile, args.relayer_ledger] + + if all(signing_methods): + raise InvalidArgumentsError( + "Only one of --relayer-pem, --relayer-keyfile, or --relayer-ledger must be provided" + ) + + if not any(signing_methods): + raise InvalidArgumentsError("One of --relayer-pem, --relayer-keyfile, or --relayer-ledger must be provided") + + +def ensure_broadcast_args(args: Any): + if args.send and args.simulate: + raise InvalidArgumentsError("Cannot both 'simulate' and 'send' a transaction") + + +def ensure_chain_id_args(args: Any): + if not args.chain and not args.proxy: + raise InvalidArgumentsError("Either --chain or --proxy must be provided") + + +def ensure_proxy_argument(args: Any): + if not args.proxy: + raise InvalidArgumentsError("--proxy must be provided") diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index f141cd58..f2168da3 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -14,6 +14,13 @@ from multiversx_sdk.abi import Abi from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.args_validation import ( + ensure_broadcast_args, + ensure_chain_id_args, + ensure_proxy_argument, + ensure_required_transaction_args_are_provided, + ensure_wallet_args_are_provided, +) from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS @@ -271,7 +278,7 @@ def _add_bytecode_arg(sub: Any): def _add_contract_arg(sub: Any): - sub.add_argument("contract", help="🖄 the address of the Smart Contract") + sub.add_argument("contract", type=str, help="🖄 the bech32 address of the Smart Contract") def _add_contract_abi_arg(sub: Any): @@ -336,9 +343,11 @@ def build(args: Any): def deploy(args: Any): logger.debug("deploy") - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) + + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) sender = cli_shared.prepare_account(args) @@ -353,7 +362,9 @@ def deploy(args: Any): relayer = cli_shared.load_relayer_account(args) relayer_address = cli_shared.get_relayer_address(relayer, args) - config = TransactionsFactoryConfig(args.chain) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) + abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) @@ -392,9 +403,11 @@ def deploy(args: Any): def call(args: Any): logger.debug("call") - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) + + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) sender = cli_shared.prepare_account(args) @@ -409,7 +422,9 @@ def call(args: Any): relayer = cli_shared.load_relayer_account(args) relayer_address = cli_shared.get_relayer_address(relayer, args) - config = TransactionsFactoryConfig(args.chain) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) + abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) @@ -441,9 +456,11 @@ def call(args: Any): def upgrade(args: Any): logger.debug("upgrade") - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) + + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) sender = cli_shared.prepare_account(args) @@ -458,7 +475,9 @@ def upgrade(args: Any): relayer = cli_shared.load_relayer_account(args) relayer_address = cli_shared.get_relayer_address(relayer, args) - config = TransactionsFactoryConfig(args.chain) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) + abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) @@ -494,6 +513,8 @@ def upgrade(args: Any): def query(args: Any): logger.debug("query") + ensure_proxy_argument(args) + # we don't need chainID to query a contract; we use the provided proxy factory_config = TransactionsFactoryConfig("") abi = Abi.load(Path(args.abi)) if args.abi else None diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 0792aca9..f944fcdc 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -1,4 +1,4 @@ -from typing import Any, List +from typing import Any from multiversx_sdk import ProxyNetworkProvider, TransactionsFactoryConfig @@ -7,7 +7,7 @@ from multiversx_sdk_cli.delegation import DelegationOperations -def setup_parser(args: List[str], subparsers: Any) -> Any: +def setup_parser(args: list[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser(subparsers, "staking-provider", "Staking provider omnitool") subparsers = parser.add_subparsers() @@ -352,7 +352,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: return subparsers -def _add_common_arguments(args: List[str], sub: Any): +def _add_common_arguments(args: list[str], sub: Any): cli_shared.add_proxy_arg(sub) cli_shared.add_wallet_args(args, sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False, with_estimate_gas=True) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 71b9a0d2..4071ffe1 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -1,10 +1,9 @@ import argparse import ast -import copy import sys from argparse import FileType from pathlib import Path -from typing import Any, Dict, List, Text, Union, cast +from typing import Any, Text, Union, cast from multiversx_sdk import ( Account, @@ -70,7 +69,7 @@ def add_command_subparser(subparsers: Any, group: str, command: str, description def add_tx_args( - args: List[str], + args: list[str], sub: Any, with_nonce: bool = True, with_receiver: bool = True, @@ -81,7 +80,7 @@ def add_tx_args( sub.add_argument( "--nonce", type=int, - required=not ("--recall-nonce" in args), + required=False, default=None, help="# the nonce for the transaction", ) @@ -93,7 +92,7 @@ def add_tx_args( ) if with_receiver: - sub.add_argument("--receiver", required=True, help="🖄 the address of the receiver") + sub.add_argument("--receiver", required=False, help="🖄 the address of the receiver") sub.add_argument("--receiver-username", required=False, help="🖄 the username of the receiver") sub.add_argument( @@ -102,7 +101,7 @@ def add_tx_args( type=int, help="⛽ the gas price (default: %(default)d)", ) - sub.add_argument("--gas-limit", required=not ("--estimate-gas" in args), type=int, help="⛽ the gas limit") + sub.add_argument("--gas-limit", required=False, type=int, help="⛽ the gas limit") if with_estimate_gas: sub.add_argument( @@ -121,7 +120,7 @@ def add_tx_args( help="the payload, or 'memo' of the transaction (default: %(default)s)", ) - sub.add_argument("--chain", help="the chain identifier") + sub.add_argument("--chain", type=str, help="the chain identifier") sub.add_argument( "--version", type=int, @@ -152,10 +151,10 @@ def add_guardian_args(sub: Any): ) -def add_wallet_args(args: List[str], sub: Any, skip_required_check: bool = False): +def add_wallet_args(args: list[str], sub: Any): sub.add_argument( "--pem", - required=check_if_sign_method_required(args, "--pem", skip_required_check), + required=False, help="🔑 the PEM file, if keyfile not provided", ) sub.add_argument( @@ -166,7 +165,7 @@ def add_wallet_args(args: List[str], sub: Any, skip_required_check: bool = False ) sub.add_argument( "--keyfile", - required=check_if_sign_method_required(args, "--keyfile", skip_required_check), + required=False, help="🔑 a JSON keyfile, if PEM not provided", ) sub.add_argument( @@ -182,7 +181,7 @@ def add_wallet_args(args: List[str], sub: Any, skip_required_check: bool = False sub.add_argument( "--ledger", action="store_true", - required=check_if_sign_method_required(args, "--ledger", skip_required_check), + required=False, default=False, help="🔐 bool flag for signing transaction using ledger", ) @@ -195,7 +194,7 @@ def add_wallet_args(args: List[str], sub: Any, skip_required_check: bool = False sub.add_argument("--sender-username", required=False, help="🖄 the username of the sender") -def add_guardian_wallet_args(args: List[str], sub: Any): +def add_guardian_wallet_args(args: list[str], sub: Any): sub.add_argument( "--guardian-pem", help="🔑 the PEM file, if keyfile not provided", @@ -234,7 +233,7 @@ def add_guardian_wallet_args(args: List[str], sub: Any): ) -def add_relayed_v3_wallet_args(args: List[str], sub: Any): +def add_relayed_v3_wallet_args(args: list[str], sub: Any): sub.add_argument("--relayer-pem", help="🔑 the PEM file, if keyfile not provided") sub.add_argument( "--relayer-pem-index", @@ -268,7 +267,7 @@ def add_relayed_v3_wallet_args(args: List[str], sub: Any): def add_proxy_arg(sub: Any): - sub.add_argument("--proxy", help="🔗 the URL of the proxy") + sub.add_argument("--proxy", type=str, help="🔗 the URL of the proxy") def add_outfile_arg(sub: Any, what: str = ""): @@ -305,10 +304,10 @@ def add_token_transfers_args(sub: Any): ) -def parse_omit_fields_arg(args: Any) -> List[str]: +def parse_omit_fields_arg(args: Any) -> list[str]: literal = args.omit_fields parsed = ast.literal_eval(literal) - return cast(List[str], parsed) + return cast(list[str], parsed) def prepare_account(args: Any): @@ -497,6 +496,26 @@ def prepare_chain_id_in_args(args: Any): args.chain = proxy.get_network_config().chain_id +def get_chain_id(chain_id: str, proxy_url: str) -> str: + if chain_id and proxy_url: + network_provider_config = config.get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) + fetched_chain_id = proxy.get_network_config().chain_id + + if chain_id != fetched_chain_id: + show_warning( + f"The chain ID you have provided does not match the chain ID you got from the proxy. Will use the proxy's value: '{fetched_chain_id}'" + ) + return fetched_chain_id + + if chain_id: + return chain_id + else: + network_provider_config = config.get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) + return proxy.get_network_config().chain_id + + def add_broadcast_args(sub: Any, simulate: bool = True): sub.add_argument( "--send", @@ -572,41 +591,3 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL ) return output_builder - - -def check_if_sign_method_required(args: List[str], checked_method: str, skip_required_check: bool = False) -> bool: - if skip_required_check: - return False - - methods = ["--pem", "--keyfile", "--ledger"] - rest_of_methods: List[str] = [] - for method in methods: - if method != checked_method: - rest_of_methods.append(method) - - for method in rest_of_methods: - if utils.is_arg_present(args, method): - return False - - return True - - -def convert_args_object_to_args_list(args: Any) -> List[str]: - arguments = copy.deepcopy(args) - args_dict: Dict[str, Any] = arguments.__dict__ - - # delete the function key because we don't need to pass it along - args_dict.pop("func", None) - - args_list: List[str] = [] - for key, val in args_dict.items(): - modified_key = "--" + key.replace("_", "-") - - if isinstance(val, bool) and val: - args_list.extend([modified_key]) - continue - - if val: - args_list.extend([modified_key, val]) - - return args_list diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 02b79e2b..ec722457 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -12,10 +12,18 @@ ) from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.args_validation import ( + ensure_broadcast_args, + ensure_chain_id_args, + ensure_proxy_argument, + ensure_relayer_wallet_args_are_provided, + ensure_required_transaction_args_are_provided, + ensure_wallet_args_are_provided, +) from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers -from multiversx_sdk_cli.errors import BadUsage, IncorrectWalletError, NoWalletProvided +from multiversx_sdk_cli.errors import BadUsage, IncorrectWalletError from multiversx_sdk_cli.transactions import ( TransactionsController, load_transaction_from_file, @@ -72,8 +80,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "sign", f"Sign a previously saved transaction.{CLIOutputBuilder.describe()}", ) - # we add the wallet args, but don't make the args mandatory - cli_shared.add_wallet_args(args=args, sub=sub, skip_required_check=True) + cli_shared.add_wallet_args(args=args, sub=sub) cli_shared.add_infile_arg(sub, what="a previously saved transaction") cli_shared.add_outfile_arg(sub, what="the signed transaction") cli_shared.add_broadcast_args(sub) @@ -107,9 +114,10 @@ def _add_common_arguments(args: list[str], sub: Any): def create_transaction(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) sender = cli_shared.prepare_account(args) @@ -131,7 +139,9 @@ def create_transaction(args: Any): transfers = getattr(args, "token_transfers", None) transfers = prepare_token_transfers(transfers) if transfers else None - tx_controller = TransactionsController(args.chain) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + tx_controller = TransactionsController(chain_id) + tx = tx_controller.create_transaction( sender=sender, receiver=Address.new_from_bech32(args.receiver), @@ -171,6 +181,8 @@ def prepare_token_transfers(transfers: list[Any]) -> list[TokenTransfer]: def send_transaction(args: Any): + ensure_proxy_argument(args) + tx = load_transaction_from_file(args.infile) output = CLIOutputBuilder() @@ -186,7 +198,7 @@ def send_transaction(args: Any): def sign_transaction(args: Any): - cli_shared.check_broadcast_args(args) + ensure_broadcast_args(args) tx = load_transaction_from_file(args.infile) @@ -221,10 +233,8 @@ def sign_transaction(args: Any): def relay_transaction(args: Any): - if not _is_relayer_wallet_provided(args): - raise NoWalletProvided() - - cli_shared.check_broadcast_args(args) + ensure_relayer_wallet_args_are_provided(args) + ensure_broadcast_args(args) tx = load_transaction_from_file(args.infile) relayer = cli_shared.prepare_relayer_account(args) @@ -235,7 +245,3 @@ def relay_transaction(args: Any): tx.relayer_signature = relayer.sign_transaction(tx) cli_shared.send_or_simulate(tx, args) - - -def _is_relayer_wallet_provided(args: Any): - return any([args.relayer_pem, args.relayer_keyfile, args.relayer_ledger]) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index eae67200..ecb7ddca 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -4,6 +4,12 @@ from multiversx_sdk import Address from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.args_validation import ( + ensure_broadcast_args, + ensure_chain_id_args, + ensure_required_transaction_args_are_provided, + ensure_wallet_args_are_provided, +) from multiversx_sdk_cli.validators.core import ValidatorsController @@ -192,9 +198,10 @@ def do_stake(args: Any): def validate_args(args: Any) -> None: - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) def prepare_sender(args: Any): diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index ed744061..558e720a 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -137,7 +137,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub = cli_shared.add_command_subparser(subparsers, "wallet", "sign-message", "Sign a message") sub.add_argument("--message", required=True, help="the message you want to sign") - cli_shared.add_wallet_args(args=args, sub=sub, skip_required_check=True) + cli_shared.add_wallet_args(args=args, sub=sub) sub.set_defaults(func=sign_user_message) sub = cli_shared.add_command_subparser(subparsers, "wallet", "verify-message", "Verify a previously signed message") diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation/staking_provider.py index 3f48585e..2145b9f7 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Any, List, Tuple +from typing import Any from multiversx_sdk import ( Address, @@ -414,7 +414,7 @@ def prepare_transaction_for_creating_delegation_contract_from_validator( return tx - def _load_validators_public_keys(self, args: Any) -> List[ValidatorPublicKey]: + def _load_validators_public_keys(self, args: Any) -> list[ValidatorPublicKey]: if args.bls_keys: return self._parse_public_bls_keys(args.bls_keys) @@ -422,24 +422,24 @@ def _load_validators_public_keys(self, args: Any) -> List[ValidatorPublicKey]: validators_file = ValidatorsFile(validators_file_path) return validators_file.load_public_keys() - def _parse_public_bls_keys(self, public_bls_keys: str) -> List[ValidatorPublicKey]: + def _parse_public_bls_keys(self, public_bls_keys: str) -> list[ValidatorPublicKey]: keys = public_bls_keys.split(",") - validator_public_keys: List[ValidatorPublicKey] = [] + validator_public_keys: list[ValidatorPublicKey] = [] for key in keys: validator_public_keys.append(ValidatorPublicKey(bytes.fromhex(key))) return validator_public_keys - def _get_public_keys_and_signed_messages(self, args: Any) -> Tuple[List[ValidatorPublicKey], List[bytes]]: + def _get_public_keys_and_signed_messages(self, args: Any) -> tuple[list[ValidatorPublicKey], list[bytes]]: validators_file_path = Path(args.validators_file).expanduser() validators_file = ValidatorsFile(validators_file_path) signers = validators_file.load_signers() pubkey = Address.new_from_bech32(args.delegation_contract).get_public_key() - public_keys: List[ValidatorPublicKey] = [] - signed_messages: List[bytes] = [] + public_keys: list[ValidatorPublicKey] = [] + signed_messages: list[bytes] = [] for signer in signers: signed_message = signer.sign(pubkey) diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 44a52369..7b3869c6 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -9,6 +9,12 @@ ) from multiversx_sdk_cli import cli_shared +from multiversx_sdk_cli.args_validation import ( + ensure_broadcast_args, + ensure_chain_id_args, + ensure_required_transaction_args_are_provided, + ensure_wallet_args_are_provided, +) from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX from multiversx_sdk_cli.transactions import TransactionsController @@ -55,9 +61,10 @@ def validate_name(name: str, shard_id: int, proxy: INetworkProvider): def register(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) sender = cli_shared.prepare_account(args) @@ -77,7 +84,8 @@ def register(args: Any): receiver = dns_address_for_name(args.name) data = dns_register_data(args.name) - controller = TransactionsController(args.chain) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + controller = TransactionsController(chain_id) tx = controller.create_transaction( sender=sender, diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 4a8ff409..e00e42e1 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -180,3 +180,8 @@ def __init__(self, message: str, inner: Any = None): class IncorrectWalletError(KnownError): def __init__(self, message: str): super().__init__(message) + + +class InvalidArgumentsError(KnownError): + def __init__(self, message: str): + super().__init__(message) diff --git a/multiversx_sdk_cli/tests/test_cli_shared.py b/multiversx_sdk_cli/tests/test_cli_shared.py deleted file mode 100644 index 4a1b1107..00000000 --- a/multiversx_sdk_cli/tests/test_cli_shared.py +++ /dev/null @@ -1,50 +0,0 @@ -from multiversx_sdk_cli.cli_shared import convert_args_object_to_args_list - - -class ContractBuildArgs: - def __init__(self) -> None: - self.verbose = False - self.path = "testadata-out/SANDBOX/myadder-rs" - self.no_wasm_opt = False - self.wasm_symbols = False - self.wasm_name = None - self.wasm_suffix = None - self.target_dir = None - self.wat = False - self.mir = False - self.llvm_ir = False - self.ignore = None - self.no_imports = False - self.no_abi_git_version = False - self.twiggy_top = False - self.twiggy_paths = False - self.twiggy_monos = False - self.twiggy_dominators = False - self.func = "should be a func object" - - -def test_args_obj_to_list(): - contract_build_args = ContractBuildArgs() - - args_list = convert_args_object_to_args_list(contract_build_args) - - assert len(args_list) == 2 - assert args_list[0] == "--path" - assert args_list[1] == contract_build_args.path - - contract_build_args.no_wasm_opt = True - args_list = convert_args_object_to_args_list(contract_build_args) - - assert len(args_list) == 3 - assert args_list[0] == "--path" - assert args_list[1] == contract_build_args.path - assert args_list[2] == "--no-wasm-opt" - - contract_build_args.ignore = "random_directory" # type: ignore - contract_build_args.no_imports = True - args_list = convert_args_object_to_args_list(contract_build_args) - - assert len(args_list) == 6 - assert args_list[3] == "--ignore" - assert args_list[4] == contract_build_args.ignore - assert args_list[5] == "--no-imports" From 4676c6a393ac9679159b4c5fd26e9e077bbafaf8 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Sat, 22 Feb 2025 19:14:20 +0200 Subject: [PATCH 50/84] refactoring delegation transactions --- multiversx_sdk_cli/args_validation.py | 3 + multiversx_sdk_cli/cli_delegation.py | 748 ++++++++++++-- .../delegation/staking_provider.py | 976 +++++++++++++----- 3 files changed, 1346 insertions(+), 381 deletions(-) diff --git a/multiversx_sdk_cli/args_validation.py b/multiversx_sdk_cli/args_validation.py index 56072e70..972c931b 100644 --- a/multiversx_sdk_cli/args_validation.py +++ b/multiversx_sdk_cli/args_validation.py @@ -58,6 +58,9 @@ def ensure_broadcast_args(args: Any): if args.send and args.simulate: raise InvalidArgumentsError("Cannot both 'simulate' and 'send' a transaction") + if args.send or args.simulate: + ensure_proxy_argument(args) + def ensure_chain_id_args(args: Any): if not args.chain and not args.proxy: diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index f944fcdc..c0e91ba1 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -1,10 +1,27 @@ +from pathlib import Path from typing import Any -from multiversx_sdk import ProxyNetworkProvider, TransactionsFactoryConfig +from multiversx_sdk import ( + Address, + DelegationTransactionsOutcomeParser, + ProxyNetworkProvider, + TransactionsFactoryConfig, + ValidatorPublicKey, +) from multiversx_sdk_cli import cli_shared, errors, utils +from multiversx_sdk_cli.args_validation import ( + ensure_broadcast_args, + ensure_chain_id_args, + ensure_proxy_argument, + ensure_required_transaction_args_are_provided, + ensure_wallet_args_are_provided, +) from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.delegation import DelegationOperations +from multiversx_sdk_cli.validators.validators_file import ValidatorsFile + +MINIMUM_AMOUNT_TO_DELEGATE = 1_000_000_000_000_000_000 # 1 EGLD def setup_parser(args: list[str], subparsers: Any) -> Any: @@ -23,9 +40,10 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--total-delegation-cap", required=True, + type=int, help="the total delegation contract capacity", ) - sub.add_argument("--service-fee", required=True, help="the delegation contract service fee") + sub.add_argument("--service-fee", required=True, type=int, help="the delegation contract service fee") sub.set_defaults(func=do_create_delegation_contract) # get contract address @@ -35,8 +53,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "get-contract-address", "Get create contract address by transaction hash", ) - sub.add_argument("--create-tx-hash", required=True, help="the hash") - sub.add_argument("--sender", required=False, help="the sender address") + sub.add_argument("--create-tx-hash", required=True, type=str, help="the hash") cli_shared.add_proxy_arg(sub) sub.set_defaults(func=get_contract_address_by_deploy_tx_hash) @@ -47,11 +64,12 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "add-nodes", "Add new nodes must be called by the contract owner", ) - sub.add_argument("--validators-file", required=True, help="a JSON file describing the Nodes") + sub.add_argument("--validators-file", required=True, type=str, help="a JSON file describing the Nodes") sub.add_argument( "--delegation-contract", required=True, - help="address of the delegation contract", + type=str, + help="bech32 address of the delegation contract", ) _add_common_arguments(args, sub) sub.set_defaults(func=add_new_nodes) @@ -68,6 +86,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--delegation-contract", required=True, + type=str, help="address of the delegation contract", ) _add_common_arguments(args, sub) @@ -85,7 +104,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--delegation-contract", required=True, - help="address of the delegation contract", + type=str, + help="bech32 address of the delegation contract", ) _add_common_arguments(args, sub) sub.set_defaults(func=stake_nodes) @@ -223,7 +243,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "change-service-fee", "Change service fee must be called by the contract owner", ) - sub.add_argument("--service-fee", required=True, help="new service fee value") + sub.add_argument("--service-fee", required=True, type=int, help="new service fee value") sub.add_argument( "--delegation-contract", required=True, @@ -312,11 +332,12 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "Set metadata must be called by the contract owner", ) - sub.add_argument("--name", required=True, help="name field in staking provider metadata") - sub.add_argument("--website", required=True, help="website field in staking provider metadata") + sub.add_argument("--name", required=True, type=str, help="name field in staking provider metadata") + sub.add_argument("--website", required=True, type=str, help="website field in staking provider metadata") sub.add_argument( "--identifier", required=True, + type=str, help="identifier field in staking provider metadata", ) sub.add_argument( @@ -338,11 +359,13 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--max-cap", required=True, + type=int, help="total delegation cap in EGLD, fully denominated. Use value 0 for uncapped", ) sub.add_argument( "--fee", required=True, + type=int, help="service fee as hundredths of percents. (e.g. a service fee of 37.45 percent is expressed by the integer 3745)", ) _add_common_arguments(args, sub) @@ -369,66 +392,233 @@ def ensure_arguments_are_provided_and_prepared(args: Any): cli_shared.prepare_nonce_in_args(args) +def prepare_sender(args: Any): + sender = cli_shared.prepare_account(args) + nonce = ( + int(args.nonce) + if args.nonce is not None + else cli_shared.get_current_nonce_for_address(sender.address, args.proxy) + ) + return sender, nonce + + +def prepare_guardian(args: Any): + guardian = cli_shared.load_guardian_account(args) + guardian_address = cli_shared.get_guardian_address(guardian, args) + return guardian, guardian_address + + +def prepare_relayer(args: Any): + relayer = cli_shared.load_relayer_account(args) + relayer_address = cli_shared.get_relayer_address(relayer, args) + return relayer, relayer_address + + def do_create_delegation_contract(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_new_delegation_contract(sender, args) + gas_limit = args.gas_limit if args.gas_limit else 0 + + tx = delegation.prepare_transaction_for_new_delegation_contract( + owner=sender, + native_amount=int(args.value), + total_delegation_cap=int(args.total_delegation_cap), + service_fee=int(args.service_fee), + gas_limit=gas_limit, + gas_price=int(args.gas_price), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def get_contract_address_by_deploy_tx_hash(args: Any): - args = utils.as_object(args) + ensure_proxy_argument(args) config = get_config_for_network_providers() proxy = ProxyNetworkProvider(url=args.proxy, config=config) - transaction = proxy.get_transaction(args.create_tx_hash) - transaction_events = transaction.logs.events - if len(transaction_events) == 1: - contract_address = transaction_events[0].address - print(contract_address.to_bech32()) - else: - raise errors.ProgrammingError( - "Tx has more than one event. Make sure it's a staking provider SC Deploy transaction." - ) + + parser = DelegationTransactionsOutcomeParser() + outcome = parser.parse_create_new_delegation_contract(transaction) + + if len(outcome) > 1: + print("This transaction created more than one delegation contract.") + + for i in range(len(outcome)): + print(f"Delegation contract address: {outcome[i].contract_address.to_bech32()}") def add_new_nodes(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_adding_nodes(sender, args) + gas_limit = 0 if args.estimate_gas else args.gas_limit + public_keys, signed_messages = _get_public_keys_and_signed_messages(args) + + tx = delegation.prepare_transaction_for_adding_nodes( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + public_keys=public_keys, + signed_messages=signed_messages, + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) +def _get_public_keys_and_signed_messages(args: Any) -> tuple[list[ValidatorPublicKey], list[bytes]]: + validators_file_path = Path(args.validators_file).expanduser() + validators_file = ValidatorsFile(validators_file_path) + signers = validators_file.load_signers() + + pubkey = Address.new_from_bech32(args.delegation_contract).get_public_key() + + public_keys: list[ValidatorPublicKey] = [] + signed_messages: list[bytes] = [] + for signer in signers: + signed_message = signer.sign(pubkey) + + public_keys.append(signer.secret_key.generate_public_key()) + signed_messages.append(signed_message) + + return public_keys, signed_messages + + def remove_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_removing_nodes(sender, args) + gas_limit = 0 if args.estimate_gas else args.gas_limit + public_keys = _load_validators_public_keys(args) + + tx = delegation.prepare_transaction_for_removing_nodes( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + public_keys=public_keys, + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) +def _load_validators_public_keys(args: Any) -> list[ValidatorPublicKey]: + if args.bls_keys: + return _parse_public_bls_keys(args.bls_keys) + + validators_file_path = Path(args.validators_file).expanduser() + validators_file = ValidatorsFile(validators_file_path) + return validators_file.load_public_keys() + + +def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: + keys = public_bls_keys.split(",") + validator_public_keys: list[ValidatorPublicKey] = [] + + for key in keys: + validator_public_keys.append(ValidatorPublicKey(bytes.fromhex(key))) + + return validator_public_keys + + def stake_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_staking_nodes(sender, args) + gas_limit = 0 if args.estimate_gas else args.gas_limit + public_keys = _load_validators_public_keys(args) + + tx = delegation.prepare_transaction_for_staking_nodes( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + public_keys=public_keys, + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) @@ -442,162 +632,528 @@ def _check_if_either_bls_keys_or_validators_file_are_provided(args: Any): def unbond_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_unbonding_nodes(sender, args) + gas_limit = 0 if args.estimate_gas else args.gas_limit + public_keys = _load_validators_public_keys(args) + + tx = delegation.prepare_transaction_for_unbonding_nodes( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + public_keys=public_keys, + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def unstake_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_unstaking_nodes(sender, args) + gas_limit = 0 if args.estimate_gas else args.gas_limit + public_keys = _load_validators_public_keys(args) + + tx = delegation.prepare_transaction_for_unstaking_nodes( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + public_keys=public_keys, + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def unjail_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_unjailing_nodes(sender, args) + gas_limit = 0 if args.estimate_gas else args.gas_limit + public_keys = _load_validators_public_keys(args) + + tx = delegation.prepare_transaction_for_unjailing_nodes( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + public_keys=public_keys, + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def delegate(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - if not (int(args.value)): - raise errors.BadUrlError("Value not provided. Minimum value to delegate is 1 EGLD") + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + gas_limit = 0 if args.estimate_gas else args.gas_limit + + value = int(args.value) + if value < MINIMUM_AMOUNT_TO_DELEGATE: + errors.BadUserInput(f"Value must be greater than {MINIMUM_AMOUNT_TO_DELEGATE} (1 EGLD)") + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_delegating(sender, args) + tx = delegation.prepare_transaction_for_delegating( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=value, + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def claim_rewards(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_claiming_rewards(sender, args) + gas_limit = 0 if args.estimate_gas else args.gas_limit + + tx = delegation.prepare_transaction_for_claiming_rewards( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def redelegate_rewards(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_redelegating_rewards(sender, args) + gas_limit = 0 if args.estimate_gas else args.gas_limit + + tx = delegation.prepare_transaction_for_redelegating_rewards( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) cli_shared.send_or_simulate(tx, args) def undelegate(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - if not (int(args.value)): - raise errors.BadUrlError("Value not provided. Minimum value to undelegate is 1 EGLD") + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + gas_limit = 0 if args.estimate_gas else args.gas_limit + + value = int(args.value) + if value < MINIMUM_AMOUNT_TO_DELEGATE: + errors.BadUserInput(f"Value must be greater than {MINIMUM_AMOUNT_TO_DELEGATE} (1 EGLD)") + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_undelegating(sender, args) + tx = delegation.prepare_transaction_for_undelegating( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=value, + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def withdraw(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + gas_limit = 0 if args.estimate_gas else args.gas_limit + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_withdrawing(sender, args) + tx = delegation.prepare_transaction_for_withdrawing( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def change_service_fee(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + gas_limit = 0 if args.estimate_gas else args.gas_limit + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_changing_service_fee(sender, args) + tx = delegation.prepare_transaction_for_changing_service_fee( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + service_fee=int(args.service_fee), + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def modify_delegation_cap(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + gas_limit = 0 if args.estimate_gas else args.gas_limit + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_modifying_delegation_cap(sender, args) + tx = delegation.prepare_transaction_for_modifying_delegation_cap( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + delegation_cap=int(args.delegation_cap), + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def automatic_activation(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + gas_limit = 0 if args.estimate_gas else args.gas_limit + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_automatic_activation(sender, args) + tx = delegation.prepare_transaction_for_automatic_activation( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + set=args.set, + unset=args.unset, + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def redelegate_cap(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + gas_limit = 0 if args.estimate_gas else args.gas_limit + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_redelegate_cap(sender, args) + tx = delegation.prepare_transaction_for_redelegate_cap( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + set=args.set, + unset=args.unset, + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def set_metadata(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + gas_limit = 0 if args.estimate_gas else args.gas_limit + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_setting_metadata(sender, args) + tx = delegation.prepare_transaction_for_setting_metadata( + owner=sender, + delegation_contract=Address.new_from_bech32(args.delegation_contract), + name=args.name, + website=args.website, + identifier=args.identifier, + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) def make_new_contract_from_validator_data(args: Any): - ensure_arguments_are_provided_and_prepared(args) + ensure_required_transaction_args_are_provided(args) + ensure_wallet_args_are_provided(args) + ensure_broadcast_args(args) + ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - config = TransactionsFactoryConfig(args.chain) + sender, nonce = prepare_sender(args) + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + + gas_limit = 0 if args.estimate_gas else args.gas_limit + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - tx = delegation.prepare_transaction_for_creating_delegation_contract_from_validator(sender, args) + tx = delegation.prepare_transaction_for_creating_delegation_contract_from_validator( + owner=sender, + max_cap=args.max_cap, + service_fee=args.fee, + gas_limit=gas_limit, + gas_price=int(args.gas_price), + value=int(args.value), + nonce=nonce, + version=int(args.version), + options=int(args.options), + guardian_account=guardian, + guardian_address=guardian_address, + relayer_account=relayer, + relayer_address=relayer_address, + guardian_service_url=args.guardian_service_url, + guardian_2fa_code=args.guardian_2fa_code, + ) + cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation/staking_provider.py index 2145b9f7..748405a7 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -1,5 +1,4 @@ -from pathlib import Path -from typing import Any +from typing import Optional from multiversx_sdk import ( Address, @@ -10,390 +9,821 @@ ) from multiversx_sdk.abi import BigUIntValue, Serializer +from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.errors import BadUsage from multiversx_sdk_cli.interfaces import IAccount -from multiversx_sdk_cli.validators.validators_file import ValidatorsFile +DELEGATION_MANAGER_SC_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000004ffff" -class DelegationOperations: + +class DelegationOperations(BaseTransactionsController): def __init__(self, config: TransactionsFactoryConfig) -> None: self._factory = DelegationTransactionsFactory(config) - def prepare_transaction_for_new_delegation_contract(self, owner: IAccount, args: Any) -> Transaction: + def prepare_transaction_for_new_delegation_contract( + self, + owner: IAccount, + native_amount: int, + total_delegation_cap: int, + service_fee: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_new_delegation_contract( sender=owner.address, - total_delegation_cap=int(args.total_delegation_cap), - service_fee=int(args.service_fee), - amount=int(args.value), + total_delegation_cap=total_delegation_cap, + service_fee=service_fee, + amount=native_amount, + ) + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) return tx - def prepare_transaction_for_adding_nodes(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - public_keys, signed_messages = self._get_public_keys_and_signed_messages(args) - + def prepare_transaction_for_adding_nodes( + self, + owner: IAccount, + delegation_contract: Address, + public_keys: list[ValidatorPublicKey], + signed_messages: list[bytes], + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_adding_nodes( sender=owner.address, delegation_contract=delegation_contract, public_keys=public_keys, signed_messages=signed_messages, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - def prepare_transaction_for_removing_nodes(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - - public_keys = self._load_validators_public_keys(args) - + def prepare_transaction_for_removing_nodes( + self, + owner: IAccount, + delegation_contract: Address, + public_keys: list[ValidatorPublicKey], + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_removing_nodes( sender=owner.address, delegation_contract=delegation_contract, public_keys=public_keys, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - def prepare_transaction_for_staking_nodes(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - - public_keys = self._load_validators_public_keys(args) - + def prepare_transaction_for_staking_nodes( + self, + owner: IAccount, + delegation_contract: Address, + public_keys: list[ValidatorPublicKey], + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_staking_nodes( sender=owner.address, delegation_contract=delegation_contract, public_keys=public_keys, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - def prepare_transaction_for_unbonding_nodes(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - - public_keys = self._load_validators_public_keys(args) - + def prepare_transaction_for_unbonding_nodes( + self, + owner: IAccount, + delegation_contract: Address, + public_keys: list[ValidatorPublicKey], + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_unbonding_nodes( sender=owner.address, delegation_contract=delegation_contract, public_keys=public_keys, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - def prepare_transaction_for_unstaking_nodes(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - - public_keys = self._load_validators_public_keys(args) - + def prepare_transaction_for_unstaking_nodes( + self, + owner: IAccount, + delegation_contract: Address, + public_keys: list[ValidatorPublicKey], + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_unstaking_nodes( sender=owner.address, delegation_contract=delegation_contract, public_keys=public_keys, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - def prepare_transaction_for_unjailing_nodes(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - - public_keys = self._load_validators_public_keys(args) - amount = int(args.value) - + def prepare_transaction_for_unjailing_nodes( + self, + owner: IAccount, + delegation_contract: Address, + public_keys: list[ValidatorPublicKey], + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_unjailing_nodes( sender=owner.address, delegation_contract=delegation_contract, public_keys=public_keys, - amount=amount, + amount=value, + ) + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - tx.value = args.value - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) return tx - def prepare_transaction_for_delegating(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - + def prepare_transaction_for_delegating( + self, + owner: IAccount, + delegation_contract: Address, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_delegating( sender=owner.address, delegation_contract=delegation_contract, - amount=int(args.value), + amount=value, + ) + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) return tx - def prepare_transaction_for_claiming_rewards(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - + def prepare_transaction_for_claiming_rewards( + self, + owner: IAccount, + delegation_contract: Address, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_claiming_rewards( sender=owner.address, delegation_contract=delegation_contract ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - def prepare_transaction_for_redelegating_rewards(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - + def prepare_transaction_for_redelegating_rewards( + self, + owner: IAccount, + delegation_contract: Address, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_redelegating_rewards( sender=owner.address, delegation_contract=delegation_contract ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - def prepare_transaction_for_undelegating(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - + def prepare_transaction_for_undelegating( + self, + owner: IAccount, + delegation_contract: Address, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_undelegating( sender=owner.address, delegation_contract=delegation_contract, - amount=int(args.value), + amount=value, + ) + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) return tx - def prepare_transaction_for_withdrawing(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - + def prepare_transaction_for_withdrawing( + self, + owner: IAccount, + delegation_contract: Address, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_withdrawing( sender=owner.address, delegation_contract=delegation_contract ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - def prepare_transaction_for_changing_service_fee(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - + def prepare_transaction_for_changing_service_fee( + self, + owner: IAccount, + delegation_contract: Address, + service_fee: int, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_changing_service_fee( sender=owner.address, delegation_contract=delegation_contract, - service_fee=int(args.service_fee), + service_fee=service_fee, + ) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) return tx - def prepare_transaction_for_modifying_delegation_cap(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - + def prepare_transaction_for_modifying_delegation_cap( + self, + owner: IAccount, + delegation_contract: Address, + delegation_cap: int, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_modifying_delegation_cap( sender=owner.address, delegation_contract=delegation_contract, - delegation_cap=int(args.delegation_cap), + delegation_cap=delegation_cap, + ) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) return tx - def prepare_transaction_for_automatic_activation(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) + def prepare_transaction_for_automatic_activation( + self, + owner: IAccount, + delegation_contract: Address, + set: bool, + unset: bool, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + if set and unset: + raise BadUsage("Cannot set and unset at the same time") - if args.set: + if set: tx = self._factory.create_transaction_for_setting_automatic_activation( sender=owner.address, delegation_contract=delegation_contract ) - elif args.unset: + elif unset: tx = self._factory.create_transaction_for_unsetting_automatic_activation( sender=owner.address, delegation_contract=delegation_contract ) else: - raise BadUsage("Either `--set` or `--unset` should be provided") - - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) + raise BadUsage("Both set and unset automatic activation are False") + + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - def prepare_transaction_for_redelegate_cap(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) + def prepare_transaction_for_redelegate_cap( + self, + owner: IAccount, + delegation_contract: Address, + set: bool, + unset: bool, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + if set and unset: + raise BadUsage("Cannot set and unset at the same time") - if args.set: + if set: tx = self._factory.create_transaction_for_setting_cap_check_on_redelegate_rewards( sender=owner.address, delegation_contract=delegation_contract ) - elif args.unset: + elif unset: tx = self._factory.create_transaction_for_unsetting_cap_check_on_redelegate_rewards( sender=owner.address, delegation_contract=delegation_contract ) else: - raise BadUsage("Either `--set` or `--unset` should be provided") - - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) + raise BadUsage("Either set or unset should be True") + + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - def prepare_transaction_for_setting_metadata(self, owner: IAccount, args: Any) -> Transaction: - delegation_contract = Address.new_from_bech32(args.delegation_contract) - + def prepare_transaction_for_setting_metadata( + self, + owner: IAccount, + delegation_contract: Address, + name: str, + website: str, + identifier: str, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: tx = self._factory.create_transaction_for_setting_metadata( sender=owner.address, delegation_contract=delegation_contract, - name=args.name, - website=args.website, - identifier=args.identifier, + name=name, + website=website, + identifier=identifier, + ) + tx.value = value + tx.gas_price = gas_price + tx.nonce = nonce + tx.version = version + tx.options = options + tx.guardian = guardian_address + tx.relayer = relayer_address + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, ) - tx.nonce = int(args.nonce) - tx.version = int(args.version) - tx.options = int(args.options) - tx.guardian = args.guardian - - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) - - tx.signature = owner.sign_transaction(tx) return tx + # will be replaced in the future once it's implemented in sdk-py def prepare_transaction_for_creating_delegation_contract_from_validator( - self, owner: IAccount, args: Any + self, + owner: IAccount, + max_cap: int, + service_fee: int, + gas_limit: int, + gas_price: int, + value: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", ) -> Transaction: - receiver = Address.new_from_hex( - "000000000000000000010000000000000000000000000000000000000004ffff", - get_address_hrp(), - ) - max_cap = int(args.max_cap) - fee = int(args.fee) + receiver = Address.new_from_hex(DELEGATION_MANAGER_SC_ADDRESS_HEX, get_address_hrp()) serializer = Serializer() - data = "makeNewContractFromValidatorData@" + serializer.serialize([BigUIntValue(max_cap), BigUIntValue(fee)]) + data = "makeNewContractFromValidatorData@" + serializer.serialize( + [BigUIntValue(max_cap), BigUIntValue(service_fee)] + ) tx = Transaction( sender=owner.address, @@ -401,49 +831,25 @@ def prepare_transaction_for_creating_delegation_contract_from_validator( gas_limit=510000000, chain_id=self._factory.config.chain_id, data=data.encode(), - nonce=int(args.nonce), - version=int(args.version), - options=int(args.options), - guardian=args.guardian, + nonce=nonce, + version=version, + options=options, + guardian=guardian_address, + relayer=relayer_address, + gas_price=gas_price, + value=value, ) - if args.gas_limit: - tx.gas_limit = int(args.gas_limit) + if gas_limit: + tx.gas_limit = gas_limit - tx.signature = owner.sign_transaction(tx) + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) return tx - - def _load_validators_public_keys(self, args: Any) -> list[ValidatorPublicKey]: - if args.bls_keys: - return self._parse_public_bls_keys(args.bls_keys) - - validators_file_path = Path(args.validators_file).expanduser() - validators_file = ValidatorsFile(validators_file_path) - return validators_file.load_public_keys() - - def _parse_public_bls_keys(self, public_bls_keys: str) -> list[ValidatorPublicKey]: - keys = public_bls_keys.split(",") - validator_public_keys: list[ValidatorPublicKey] = [] - - for key in keys: - validator_public_keys.append(ValidatorPublicKey(bytes.fromhex(key))) - - return validator_public_keys - - def _get_public_keys_and_signed_messages(self, args: Any) -> tuple[list[ValidatorPublicKey], list[bytes]]: - validators_file_path = Path(args.validators_file).expanduser() - validators_file = ValidatorsFile(validators_file_path) - signers = validators_file.load_signers() - - pubkey = Address.new_from_bech32(args.delegation_contract).get_public_key() - - public_keys: list[ValidatorPublicKey] = [] - signed_messages: list[bytes] = [] - for signer in signers: - signed_message = signer.sign(pubkey) - - public_keys.append(signer.secret_key.generate_public_key()) - signed_messages.append(signed_message) - - return public_keys, signed_messages From 5179602b64059eaea7dcd31d5f55bc984dfb50ff Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 24 Feb 2025 14:52:30 +0200 Subject: [PATCH 51/84] estimate gas limit wherever possible --- multiversx_sdk_cli/args_validation.py | 15 +- multiversx_sdk_cli/cli_contracts.py | 21 +- multiversx_sdk_cli/cli_delegation.py | 270 +++++++----------- multiversx_sdk_cli/cli_shared.py | 146 ++-------- multiversx_sdk_cli/cli_transactions.py | 30 +- multiversx_sdk_cli/cli_validators.py | 139 ++++----- multiversx_sdk_cli/dns.py | 7 +- .../tests/test_cli_staking_provider.py | 23 -- .../tests/test_cli_validators.py | 15 - .../tests/test_cli_validators_localnet.py | 13 - multiversx_sdk_cli/tests/test_shared.py | 34 --- multiversx_sdk_cli/transactions.py | 7 +- multiversx_sdk_cli/validators/core.py | 39 +-- 13 files changed, 222 insertions(+), 537 deletions(-) delete mode 100644 multiversx_sdk_cli/tests/test_shared.py diff --git a/multiversx_sdk_cli/args_validation.py b/multiversx_sdk_cli/args_validation.py index 972c931b..60be2f2b 100644 --- a/multiversx_sdk_cli/args_validation.py +++ b/multiversx_sdk_cli/args_validation.py @@ -4,12 +4,12 @@ def ensure_required_transaction_args_are_provided(args: Any): - _ensure_nonce_args(args) - _ensure_receiver_args(args) - _ensure_gas_limit_args(args) + ensure_nonce_args(args) + ensure_receiver_args(args) + ensure_gas_limit_args(args) -def _ensure_nonce_args(args: Any): +def ensure_nonce_args(args: Any): """If nonce is not provided, ensure that recall_nonce is provided. If recall_nonce is provided, ensure that proxy is provided.""" if hasattr(args, "nonce") and args.nonce is None: if not args.recall_nonce: @@ -19,17 +19,16 @@ def _ensure_nonce_args(args: Any): raise InvalidArgumentsError("--proxy must be provided if --recall-nonce is used") -def _ensure_receiver_args(args: Any): +def ensure_receiver_args(args: Any): """Ensure that receiver is provided.""" if hasattr(args, "receiver") and not args.receiver: raise InvalidArgumentsError("--receiver must be provided") -def _ensure_gas_limit_args(args: Any): +def ensure_gas_limit_args(args: Any): """Ensure that gas_limit is provided.""" if hasattr(args, "gas_limit") and not args.gas_limit: - if hasattr(args, "estimate_gas") and not args.estimate_gas: - raise InvalidArgumentsError("If --gas-limit not provided, --estimate-gas must be provided") + raise InvalidArgumentsError("--gas-limit must be provided") def ensure_wallet_args_are_provided(args: Any): diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index f2168da3..d259bf11 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -349,12 +349,7 @@ def deploy(args: Any): ensure_broadcast_args(args) ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) + sender, nonce = cli_shared.prepare_sender(args) guardian = cli_shared.load_guardian_account(args) guardian_address = cli_shared.get_guardian_address(guardian, args) @@ -409,12 +404,7 @@ def call(args: Any): ensure_broadcast_args(args) ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) + sender, nonce = cli_shared.prepare_sender(args) guardian = cli_shared.load_guardian_account(args) guardian_address = cli_shared.get_guardian_address(guardian, args) @@ -462,12 +452,7 @@ def upgrade(args: Any): ensure_broadcast_args(args) ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) + sender, nonce = cli_shared.prepare_sender(args) guardian = cli_shared.load_guardian_account(args) guardian_address = cli_shared.get_guardian_address(guardian, args) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index c0e91ba1..14e692b5 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -13,8 +13,9 @@ from multiversx_sdk_cli.args_validation import ( ensure_broadcast_args, ensure_chain_id_args, + ensure_nonce_args, ensure_proxy_argument, - ensure_required_transaction_args_are_provided, + ensure_receiver_args, ensure_wallet_args_are_provided, ) from multiversx_sdk_cli.config import get_config_for_network_providers @@ -378,51 +379,27 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: def _add_common_arguments(args: list[str], sub: Any): cli_shared.add_proxy_arg(sub) cli_shared.add_wallet_args(args, sub) - cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False, with_estimate_gas=True) + cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) cli_shared.add_broadcast_args(sub) cli_shared.add_outfile_arg(sub, what="signed transaction, hash") cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) -def ensure_arguments_are_provided_and_prepared(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - cli_shared.prepare_nonce_in_args(args) - - -def prepare_sender(args: Any): - sender = cli_shared.prepare_account(args) - nonce = ( - int(args.nonce) - if args.nonce is not None - else cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - ) - return sender, nonce - - -def prepare_guardian(args: Any): - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - return guardian, guardian_address - - -def prepare_relayer(args: Any): - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) - return relayer, relayer_address - - -def do_create_delegation_contract(args: Any): - ensure_required_transaction_args_are_provided(args) +def _ensure_required_args(args: Any): + ensure_nonce_args(args) + ensure_receiver_args(args) ensure_wallet_args_are_provided(args) ensure_broadcast_args(args) ensure_chain_id_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + +def do_create_delegation_contract(args: Any): + _ensure_required_args(args) + + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -469,20 +446,17 @@ def get_contract_address_by_deploy_tx_hash(args: Any): def add_new_nodes(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 public_keys, signed_messages = _get_public_keys_and_signed_messages(args) tx = delegation.prepare_transaction_for_adding_nodes( @@ -526,21 +500,18 @@ def _get_public_keys_and_signed_messages(args: Any) -> tuple[list[ValidatorPubli def remove_nodes(args: Any): + _ensure_required_args(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 public_keys = _load_validators_public_keys(args) tx = delegation.prepare_transaction_for_removing_nodes( @@ -584,21 +555,18 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def stake_nodes(args: Any): + _ensure_required_args(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 public_keys = _load_validators_public_keys(args) tx = delegation.prepare_transaction_for_staking_nodes( @@ -631,21 +599,18 @@ def _check_if_either_bls_keys_or_validators_file_are_provided(args: Any): def unbond_nodes(args: Any): + _ensure_required_args(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 public_keys = _load_validators_public_keys(args) tx = delegation.prepare_transaction_for_unbonding_nodes( @@ -670,21 +635,18 @@ def unbond_nodes(args: Any): def unstake_nodes(args: Any): + _ensure_required_args(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 public_keys = _load_validators_public_keys(args) tx = delegation.prepare_transaction_for_unstaking_nodes( @@ -709,21 +671,18 @@ def unstake_nodes(args: Any): def unjail_nodes(args: Any): + _ensure_required_args(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 public_keys = _load_validators_public_keys(args) tx = delegation.prepare_transaction_for_unjailing_nodes( @@ -748,16 +707,13 @@ def unjail_nodes(args: Any): def delegate(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 value = int(args.value) if value < MINIMUM_AMOUNT_TO_DELEGATE: @@ -788,20 +744,17 @@ def delegate(args: Any): def claim_rewards(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 tx = delegation.prepare_transaction_for_claiming_rewards( owner=sender, @@ -823,20 +776,17 @@ def claim_rewards(args: Any): def redelegate_rewards(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 tx = delegation.prepare_transaction_for_redelegating_rewards( owner=sender, @@ -858,16 +808,13 @@ def redelegate_rewards(args: Any): def undelegate(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 value = int(args.value) if value < MINIMUM_AMOUNT_TO_DELEGATE: @@ -898,16 +845,13 @@ def undelegate(args: Any): def withdraw(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -934,16 +878,13 @@ def withdraw(args: Any): def change_service_fee(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -971,16 +912,13 @@ def change_service_fee(args: Any): def modify_delegation_cap(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -1008,16 +946,13 @@ def modify_delegation_cap(args: Any): def automatic_activation(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -1046,16 +981,13 @@ def automatic_activation(args: Any): def redelegate_cap(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -1084,16 +1016,13 @@ def redelegate_cap(args: Any): def set_metadata(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -1123,16 +1052,13 @@ def set_metadata(args: Any): def make_new_contract_from_validator_data(args: Any): - ensure_required_transaction_args_are_provided(args) - ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + _ensure_required_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 4071ffe1..fbfc26cb 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -74,7 +74,6 @@ def add_tx_args( with_nonce: bool = True, with_receiver: bool = True, with_data: bool = True, - with_estimate_gas: bool = False, ): if with_nonce: sub.add_argument( @@ -103,14 +102,6 @@ def add_tx_args( ) sub.add_argument("--gas-limit", required=False, type=int, help="⛽ the gas limit") - if with_estimate_gas: - sub.add_argument( - "--estimate-gas", - action="store_true", - default=False, - help="⛽ whether to estimate the gas limit (default: %(default)d)", - ) - sub.add_argument("--value", default="0", type=int, help="the value to transfer (default: %(default)s)") if with_data: @@ -329,63 +320,6 @@ def prepare_account(args: Any): raise errors.NoWalletProvided() -def prepare_relayer_account(args: Any) -> IAccount: - hrp = config.get_address_hrp() - - if args.relayer_ledger: - return LedgerAccount(address_index=args.relayer_ledger_address_index) - if args.relayer_pem: - return Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_pem_index, hrp=hrp) - elif args.relayer_keyfile: - password = load_password(args) - return Account.new_from_keystore( - file_path=Path(args.relayer_keyfile), - password=password, - address_index=args.relayer_address_index, - hrp=hrp, - ) - else: - raise errors.NoWalletProvided() - - -def prepare_guardian_account(args: Any) -> IAccount: - hrp = config.get_address_hrp() - - if args.guardian_pem: - return Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_pem_index, hrp=hrp) - elif args.guardian_keyfile: - password = load_guardian_password(args) - return Account.new_from_keystore( - file_path=Path(args.guardian_keyfile), - password=password, - address_index=args.guardian_address_index, - hrp=hrp, - ) - elif args.guardian_ledger: - return LedgerAccount(address_index=args.relayer_ledger_address_index) - else: - raise errors.NoWalletProvided() - - -def load_sender_account(args: Any) -> Union[IAccount, None]: - hrp = config.get_address_hrp() - - if args.pem: - return Account.new_from_pem(file_path=Path(args.pem), index=args.pem_index, hrp=hrp) - elif args.keyfile: - password = load_password(args) - return Account.new_from_keystore( - file_path=Path(args.keyfile), - password=password, - address_index=args.address_index, - hrp=hrp, - ) - elif args.ledger: - return LedgerAccount(address_index=args.ledger_address_index) - - return None - - def load_guardian_account(args: Any) -> Union[IAccount, None]: hrp = config.get_address_hrp() @@ -450,17 +384,6 @@ def load_relayer_account(args: Any) -> Union[IAccount, None]: return None -def prepare_nonce_in_args(args: Any): - if args.recall_nonce and not args.proxy: - raise ArgumentsNotProvidedError("When using `--recall-nonce`, `--proxy` must be provided, as well") - - if args.recall_nonce: - account = prepare_account(args) - network_provider_config = config.get_config_for_network_providers() - proxy = ProxyNetworkProvider(url=args.proxy, config=network_provider_config) - args.nonce = proxy.get_account(account.address).nonce - - def get_current_nonce_for_address(address: Address, proxy_url: Union[str, None]) -> int: if not proxy_url: raise ArgumentsNotProvidedError("If `--nonce` is not explicitly provided, `--proxy` must be provided") @@ -470,32 +393,6 @@ def get_current_nonce_for_address(address: Address, proxy_url: Union[str, None]) return proxy.get_account(address).nonce -def prepare_chain_id_in_args(args: Any): - if not args.chain and not args.proxy: - raise ArgumentsNotProvidedError("chain ID cannot be decided: `--chain` or `--proxy` should be provided") - - if args.chain and args.proxy: - network_provider_config = config.get_config_for_network_providers() - proxy = ProxyNetworkProvider(url=args.proxy, config=network_provider_config) - fetched_chain_id = proxy.get_network_config().chain_id - - if args.chain != fetched_chain_id: - show_warning( - f"The chain ID you have provided does not match the chain ID you got from the proxy. Will use the proxy's value: '{fetched_chain_id}'" - ) - args.chain = fetched_chain_id - return - # if the CLI provided chain ID is correct, we do not patch the arguments - return - - if args.chain: - return - elif args.proxy: - network_provider_config = config.get_config_for_network_providers() - proxy = ProxyNetworkProvider(url=args.proxy, config=network_provider_config) - args.chain = proxy.get_network_config().chain_id - - def get_chain_id(chain_id: str, proxy_url: str) -> str: if chain_id and proxy_url: network_provider_config = config.get_config_for_network_providers() @@ -533,25 +430,6 @@ def add_broadcast_args(sub: Any, simulate: bool = True): ) -def check_broadcast_args(args: Any): - if args.send and args.simulate: - raise errors.BadUsage("Cannot both 'simulate' and 'send' a transaction") - - -def check_guardian_args(args: Any): - if args.guardian: - if should_sign_with_cosigner_service(args) and should_sign_with_guardian_key(args): - raise errors.BadUsage("Guarded tx should be signed using either a cosigning service or a guardian key") - - -def should_sign_with_cosigner_service(args: Any) -> bool: - return all([args.guardian_service_url, args.guardian_2fa_code]) - - -def should_sign_with_guardian_key(args: Any) -> bool: - return any([args.guardian_pem, args.guardian_keyfile, args.guardian_ledger]) - - def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CLIOutputBuilder: network_provider_config = config.get_config_for_network_providers() proxy = ProxyNetworkProvider(url=args.proxy, config=network_provider_config) @@ -591,3 +469,27 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL ) return output_builder + + +def prepare_sender(args: Any): + """Reurns a tuple containing the sender's account and the account nonce. + If no account was provided, will raise an exception.""" + sender = prepare_account(args) + nonce = int(args.nonce) if args.nonce is not None else get_current_nonce_for_address(sender.address, args.proxy) + return sender, nonce + + +def prepare_guardian(args: Any): + """Reurns a tuple containing the guardians's account and the account's address. + If no account or address were provided, will return (None, None).""" + guardian = load_guardian_account(args) + guardian_address = get_guardian_address(guardian, args) + return guardian, guardian_address + + +def prepare_relayer(args: Any): + """Reurns a tuple containing the relayer's account and the account's address. + If no account or address were provided, will return (None, None).""" + relayer = load_relayer_account(args) + relayer_address = get_relayer_address(relayer, args) + return relayer, relayer_address diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index ec722457..71262d3e 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -15,15 +15,16 @@ from multiversx_sdk_cli.args_validation import ( ensure_broadcast_args, ensure_chain_id_args, + ensure_nonce_args, ensure_proxy_argument, + ensure_receiver_args, ensure_relayer_wallet_args_are_provided, - ensure_required_transaction_args_are_provided, ensure_wallet_args_are_provided, ) from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers -from multiversx_sdk_cli.errors import BadUsage, IncorrectWalletError +from multiversx_sdk_cli.errors import BadUsage, IncorrectWalletError, NoWalletProvided from multiversx_sdk_cli.transactions import ( TransactionsController, load_transaction_from_file, @@ -114,17 +115,13 @@ def _add_common_arguments(args: list[str], sub: Any): def create_transaction(args: Any): - ensure_required_transaction_args_are_provided(args) + ensure_nonce_args(args) + ensure_receiver_args(args) ensure_wallet_args_are_provided(args) ensure_broadcast_args(args) ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) + sender, nonce = cli_shared.prepare_sender(args) if args.data_file: args.data = Path(args.data_file).read_text() @@ -136,6 +133,8 @@ def create_transaction(args: Any): relayer_address = cli_shared.get_relayer_address(relayer, args) native_amount = int(args.value) + gas_limit = int(args.gas_limit) if args.gas_limit else 0 + transfers = getattr(args, "token_transfers", None) transfers = prepare_token_transfers(transfers) if transfers else None @@ -146,7 +145,7 @@ def create_transaction(args: Any): sender=sender, receiver=Address.new_from_bech32(args.receiver), native_amount=native_amount, - gas_limit=args.gas_limit, + gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, version=args.version, @@ -202,7 +201,11 @@ def sign_transaction(args: Any): tx = load_transaction_from_file(args.infile) - sender = cli_shared.load_sender_account(args) + try: + sender = cli_shared.prepare_account(args) + except NoWalletProvided: + sender = None + if sender and sender.address != tx.sender: raise IncorrectWalletError("Sender's wallet does not match transaction's sender.") @@ -237,7 +240,10 @@ def relay_transaction(args: Any): ensure_broadcast_args(args) tx = load_transaction_from_file(args.infile) - relayer = cli_shared.prepare_relayer_account(args) + + relayer = cli_shared.load_relayer_account(args) + if relayer is None: + raise NoWalletProvided() if tx.relayer != relayer.address: raise IncorrectWalletError("Relayer wallet does not match the relayer's address set in the transaction.") diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index ecb7ddca..2e1cf102 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -7,7 +7,8 @@ from multiversx_sdk_cli.args_validation import ( ensure_broadcast_args, ensure_chain_id_args, - ensure_required_transaction_args_are_provided, + ensure_nonce_args, + ensure_receiver_args, ensure_wallet_args_are_provided, ) from multiversx_sdk_cli.validators.core import ValidatorsController @@ -129,7 +130,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: def _add_common_arguments(args: list[str], sub: Any): cli_shared.add_proxy_arg(sub) cli_shared.add_wallet_args(args, sub) - cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False, with_estimate_gas=True) + cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) cli_shared.add_broadcast_args(sub) cli_shared.add_outfile_arg(sub, what="signed transaction, hash") cli_shared.add_guardian_wallet_args(args, sub) @@ -146,12 +147,12 @@ def _add_nodes_arg(sub: Any): def do_stake(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 rewards_address = Address.new_from_bech32(args.reward_address) if args.reward_address else None controller = ValidatorsController(args.chain) @@ -160,7 +161,6 @@ def do_stake(args: Any): tx = controller.create_transaction_for_topping_up( sender=sender, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -179,7 +179,6 @@ def do_stake(args: Any): sender=sender, validators_file=validators_file, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -198,42 +197,21 @@ def do_stake(args: Any): def validate_args(args: Any) -> None: - ensure_required_transaction_args_are_provided(args) + ensure_nonce_args(args) + ensure_receiver_args(args) ensure_wallet_args_are_provided(args) ensure_broadcast_args(args) ensure_chain_id_args(args) -def prepare_sender(args: Any): - sender = cli_shared.prepare_account(args) - nonce = ( - int(args.nonce) - if args.nonce is not None - else cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - ) - return sender, nonce - - -def prepare_guardian(args: Any): - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - return guardian, guardian_address - - -def prepare_relayer(args: Any): - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) - return relayer, relayer_address - - def do_unstake(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 keys = args.nodes_public_keys controller = ValidatorsController(args.chain) @@ -241,7 +219,6 @@ def do_unstake(args: Any): sender=sender, keys=keys, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -260,12 +237,12 @@ def do_unstake(args: Any): def do_unjail(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 keys = args.nodes_public_keys controller = ValidatorsController(args.chain) @@ -273,7 +250,6 @@ def do_unjail(args: Any): sender=sender, keys=keys, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -292,12 +268,12 @@ def do_unjail(args: Any): def do_unbond(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 keys = args.nodes_public_keys controller = ValidatorsController(args.chain) @@ -305,7 +281,6 @@ def do_unbond(args: Any): sender=sender, keys=keys, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -324,12 +299,12 @@ def do_unbond(args: Any): def change_reward_address(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 rewards_address = Address.new_from_bech32(args.reward_address) controller = ValidatorsController(args.chain) @@ -337,7 +312,6 @@ def change_reward_address(args: Any): sender=sender, rewards_address=rewards_address, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -356,18 +330,17 @@ def change_reward_address(args: Any): def do_claim(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 controller = ValidatorsController(args.chain) tx = controller.create_transaction_for_claiming( sender=sender, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -386,12 +359,12 @@ def do_claim(args: Any): def do_unstake_nodes(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 keys = args.nodes_public_keys @@ -400,7 +373,6 @@ def do_unstake_nodes(args: Any): sender=sender, keys=keys, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -419,20 +391,19 @@ def do_unstake_nodes(args: Any): def do_unstake_tokens(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) value = int(args.unstake_value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 controller = ValidatorsController(args.chain) tx = controller.create_transaction_for_unstaking_tokens( sender=sender, value=value, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -451,12 +422,12 @@ def do_unstake_tokens(args: Any): def do_unbond_nodes(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 keys = args.nodes_public_keys @@ -465,7 +436,6 @@ def do_unbond_nodes(args: Any): sender=sender, keys=keys, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -484,20 +454,19 @@ def do_unbond_nodes(args: Any): def do_unbond_tokens(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) value = int(args.unbond_value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 controller = ValidatorsController(args.chain) tx = controller.create_transaction_for_unbonding_tokens( sender=sender, value=value, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -516,18 +485,17 @@ def do_unbond_tokens(args: Any): def do_clean_registered_data(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 controller = ValidatorsController(args.chain) tx = controller.create_transaction_for_cleaning_registered_data( sender=sender, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, @@ -546,12 +514,12 @@ def do_clean_registered_data(args: Any): def do_restake_unstaked_nodes(args: Any): validate_args(args) - sender, nonce = prepare_sender(args) - guardian, guardian_address = prepare_guardian(args) - relayer, relayer_address = prepare_relayer(args) + sender, nonce = cli_shared.prepare_sender(args) + guardian, guardian_address = cli_shared.prepare_guardian(args) + relayer, relayer_address = cli_shared.prepare_relayer(args) native_amount = int(args.value) - gas_limit = 0 if args.estimate_gas else args.gas_limit + gas_limit = args.gas_limit if args.gas_limit else 0 keys = args.nodes_public_keys controller = ValidatorsController(args.chain) @@ -559,7 +527,6 @@ def do_restake_unstaked_nodes(args: Any): sender=sender, keys=keys, native_amount=native_amount, - estimate_gas=args.estimate_gas, gas_limit=gas_limit, gas_price=args.gas_price, nonce=nonce, diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 7b3869c6..44954ae9 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -66,12 +66,7 @@ def register(args: Any): ensure_broadcast_args(args) ensure_chain_id_args(args) - sender = cli_shared.prepare_account(args) - - if args.nonce is None: - nonce = cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - else: - nonce = int(args.nonce) + sender, nonce = cli_shared.prepare_sender(args) guardian = cli_shared.load_guardian_account(args) guardian_address = cli_shared.get_guardian_address(guardian, args) diff --git a/multiversx_sdk_cli/tests/test_cli_staking_provider.py b/multiversx_sdk_cli/tests/test_cli_staking_provider.py index 2e0153eb..7a280bf5 100644 --- a/multiversx_sdk_cli/tests/test_cli_staking_provider.py +++ b/multiversx_sdk_cli/tests/test_cli_staking_provider.py @@ -21,7 +21,6 @@ def test_create_new_delegation_contract(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--value", "1250000000000000000000", "--total-delegation-cap", @@ -57,7 +56,6 @@ def test_create_new_delegation_contract_with_provided_gas_limit(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--value", "1250000000000000000000", "--total-delegation-cap", @@ -103,7 +101,6 @@ def test_add_nodes(capsys: Any): "T", "--nonce", "7", - "--estimate-gas", ] ) tx = get_transaction(capsys) @@ -176,7 +173,6 @@ def test_remove_nodes_with_bls_keys(capsys: Any): "T", "--nonce", "7", - "--estimate-gas", ] ) tx = get_transaction(capsys) @@ -207,7 +203,6 @@ def test_remove_nodes_with_validators_file(capsys: Any): "T", "--nonce", "7", - "--estimate-gas", ] ) tx = get_transaction(capsys) @@ -238,7 +233,6 @@ def test_stake_nodes_with_bls_keys(capsys: Any): "T", "--nonce", "7", - "--estimate-gas", ] ) tx = get_transaction(capsys) @@ -269,7 +263,6 @@ def test_stake_nodes_with_validators_file(capsys: Any): "T", "--nonce", "7", - "--estimate-gas", ] ) tx = get_transaction(capsys) @@ -300,7 +293,6 @@ def test_unbond_nodes(capsys: Any): "T", "--nonce", "7", - "--estimate-gas", ] ) tx = get_transaction(capsys) @@ -331,7 +323,6 @@ def test_unstake_nodes(capsys: Any): "T", "--nonce", "7", - "--estimate-gas", ] ) tx = get_transaction(capsys) @@ -364,7 +355,6 @@ def test_unjail_nodes(capsys: Any): "T", "--nonce", "7", - "--estimate-gas", ] ) tx = get_transaction(capsys) @@ -396,7 +386,6 @@ def test_change_service_fee(capsys: Any): "T", "--nonce", "7", - "--estimate-gas", ] ) tx = get_transaction(capsys) @@ -424,7 +413,6 @@ def test_modify_delegation_cap(capsys: Any): "T", "--nonce", "7", - "--estimate-gas", ] ) tx = get_transaction(capsys) @@ -449,7 +437,6 @@ def test_automatic_activation(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] @@ -477,7 +464,6 @@ def test_automatic_activation(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] @@ -504,7 +490,6 @@ def test_redelegate_cap(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] @@ -532,7 +517,6 @@ def test_redelegate_cap(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] @@ -564,7 +548,6 @@ def test_set_metadata(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] @@ -592,7 +575,6 @@ def test_create_delegation_contract_from_validator(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] @@ -620,7 +602,6 @@ def test_delegate(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] @@ -646,7 +627,6 @@ def test_claim_rewards(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] @@ -672,7 +652,6 @@ def test_redelegate_rewards(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] @@ -700,7 +679,6 @@ def test_undelegate(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] @@ -726,7 +704,6 @@ def test_withdraw(capsys: Any): str(alice), "--nonce", "7", - "--estimate-gas", "--chain", "T", ] diff --git a/multiversx_sdk_cli/tests/test_cli_validators.py b/multiversx_sdk_cli/tests/test_cli_validators.py index a6bff22a..f7f85da8 100644 --- a/multiversx_sdk_cli/tests/test_cli_validators.py +++ b/multiversx_sdk_cli/tests/test_cli_validators.py @@ -32,7 +32,6 @@ def test_stake(capsys: Any): reward_address, "--chain", "localnet", - "--estimate-gas", "--nonce=0", ] ) @@ -72,7 +71,6 @@ def test_top_up(capsys: Any): "--top-up", "--chain", "localnet", - "--estimate-gas", "--nonce=0", "--reward-address", "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", @@ -116,7 +114,6 @@ def test_stake_with_relayer_and_guardian(capsys: Any): reward_address, "--chain", "localnet", - "--estimate-gas", "--nonce=0", "--options=2", "--relayer", @@ -169,7 +166,6 @@ def test_stake_top_up(capsys: Any): "2711000000000000000000", "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -205,7 +201,6 @@ def test_unstake(capsys: Any): bls_key, "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -244,7 +239,6 @@ def test_unbond(capsys: Any): bls_key, "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -285,7 +279,6 @@ def test_unjail(capsys: Any): bls_key, "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -324,7 +317,6 @@ def test_change_reward_address(capsys: Any): reward_address, "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -358,7 +350,6 @@ def test_claim(capsys: Any): str(alice_pem), "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -394,7 +385,6 @@ def test_unstake_nodes(capsys: Any): bls_key, "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -433,7 +423,6 @@ def test_unstake_tokens(capsys: Any): "11000000000000000000", "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -469,7 +458,6 @@ def test_unbond_nodes(capsys: Any): bls_key, "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -508,7 +496,6 @@ def test_unbond_tokens(capsys: Any): "20000000000000000000", "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -542,7 +529,6 @@ def test_clean_registration_data(capsys: Any): str(alice_pem), "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) @@ -578,7 +564,6 @@ def test_re_stake_unstaked_nodes(capsys: Any): bls_key, "--chain", "localnet", - "--estimate-gas", "--nonce=7", ] ) diff --git a/multiversx_sdk_cli/tests/test_cli_validators_localnet.py b/multiversx_sdk_cli/tests/test_cli_validators_localnet.py index 04576f94..7086ce05 100644 --- a/multiversx_sdk_cli/tests/test_cli_validators_localnet.py +++ b/multiversx_sdk_cli/tests/test_cli_validators_localnet.py @@ -34,7 +34,6 @@ def test_stake(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -57,7 +56,6 @@ def test_stake(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--nonce=0", ] ) @@ -80,7 +78,6 @@ def test_stake_top_up(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -102,7 +99,6 @@ def test_unstake(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -124,7 +120,6 @@ def test_unbond(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -148,7 +143,6 @@ def test_unjail(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -170,7 +164,6 @@ def test_change_reward_address(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -192,7 +185,6 @@ def test_unstake_nodes(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -214,7 +206,6 @@ def test_unstake_tokens(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -236,7 +227,6 @@ def test_unbond_nodes(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -258,7 +248,6 @@ def test_unbond_tokens(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -278,7 +267,6 @@ def test_clean_registration_data(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) @@ -300,7 +288,6 @@ def test_re_stake_unstaked_nodes(): "localnet", "--proxy", "http://127.0.0.1:7950", - "--estimate-gas", "--recall-nonce", ] ) diff --git a/multiversx_sdk_cli/tests/test_shared.py b/multiversx_sdk_cli/tests/test_shared.py deleted file mode 100644 index 31208d61..00000000 --- a/multiversx_sdk_cli/tests/test_shared.py +++ /dev/null @@ -1,34 +0,0 @@ -from typing import Any - -import pytest - -from multiversx_sdk_cli.cli_shared import prepare_chain_id_in_args -from multiversx_sdk_cli.errors import ArgumentsNotProvidedError - - -class Args: - pass - - -def test_prepare_chain_id_in_args() -> None: - args: Any = Args() - args.chain = None - args.proxy = None - - with pytest.raises(ArgumentsNotProvidedError): - prepare_chain_id_in_args(args) - - args.chain = "I" - args.proxy = "https://testnet-api.multiversx.com" - - prepare_chain_id_in_args(args) - assert args.chain == "T" - - args.chain = None - prepare_chain_id_in_args(args) - assert args.chain == "T" - - args.chain = "T" - args.proxy = None - prepare_chain_id_in_args(args) - assert args.chain == "T" diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index df30cdd0..0fe2e2c8 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -17,6 +17,7 @@ from multiversx_sdk_cli import errors from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController +from multiversx_sdk_cli.constants import MIN_GAS_LIMIT from multiversx_sdk_cli.interfaces import IAccount logger = logging.getLogger("transactions") @@ -61,7 +62,7 @@ def create_transaction( transaction = Transaction( sender=sender.address, receiver=receiver, - gas_limit=gas_limit, + gas_limit=MIN_GAS_LIMIT, chain_id=self.factory.config.chain_id, ) else: @@ -73,7 +74,9 @@ def create_transaction( data=data.encode() if data else None, ) - transaction.gas_limit = gas_limit + if gas_limit: + transaction.gas_limit = gas_limit + transaction.gas_price = gas_price transaction.nonce = nonce transaction.version = version diff --git a/multiversx_sdk_cli/validators/core.py b/multiversx_sdk_cli/validators/core.py index e7c6e194..238288c7 100644 --- a/multiversx_sdk_cli/validators/core.py +++ b/multiversx_sdk_cli/validators/core.py @@ -33,7 +33,6 @@ def create_transaction_for_staking( sender: IAccount, validators_file: Path, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -54,7 +53,7 @@ def create_transaction_for_staking( rewards_address=rewards_address, ) - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.STAKE, validators.get_num_of_nodes()) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -106,7 +105,6 @@ def create_transaction_for_topping_up( self, sender: IAccount, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -122,7 +120,7 @@ def create_transaction_for_topping_up( data = "stake" receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.STAKE, 1) return self.transactions_controller.create_transaction( @@ -148,7 +146,6 @@ def create_transaction_for_unstaking( sender: IAccount, keys: str, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -164,7 +161,7 @@ def create_transaction_for_unstaking( parsed_keys, num_keys = utils.parse_keys(keys) data = f"unStake{parsed_keys}" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNSTAKE, num_keys) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -192,7 +189,6 @@ def create_transaction_for_unjailing( sender: IAccount, keys: str, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -208,7 +204,7 @@ def create_transaction_for_unjailing( parsed_keys, num_keys = utils.parse_keys(keys) data = f"unJail{parsed_keys}" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNJAIL, num_keys) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -236,7 +232,6 @@ def create_transaction_for_unbonding( sender: IAccount, keys: str, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -252,7 +247,7 @@ def create_transaction_for_unbonding( parsed_keys, num_keys = utils.parse_keys(keys) data = f"unBond{parsed_keys}" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNBOND, num_keys) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -280,7 +275,6 @@ def create_transaction_for_changing_rewards_address( sender: IAccount, rewards_address: Address, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -295,7 +289,7 @@ def create_transaction_for_changing_rewards_address( ) -> Transaction: data = f"changeRewardAddress@{rewards_address.to_hex()}" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.CHANGE_REWARD_ADDRESS) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -322,7 +316,6 @@ def create_transaction_for_claiming( self, sender: IAccount, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -337,7 +330,7 @@ def create_transaction_for_claiming( ) -> Transaction: data = "claim" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.CLAIM) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -365,7 +358,6 @@ def create_transaction_for_unstaking_nodes( sender: IAccount, keys: str, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -381,7 +373,7 @@ def create_transaction_for_unstaking_nodes( parsed_keys, num_keys = utils.parse_keys(keys) data = f"unStakeNodes{parsed_keys}" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNSTAKE, num_keys) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -409,7 +401,6 @@ def create_transaction_for_unstaking_tokens( sender: IAccount, value: int, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -424,7 +415,7 @@ def create_transaction_for_unstaking_tokens( ) -> Transaction: data = f"unStakeTokens@{self.serializer.serialize([BigUIntValue(value)])}" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNSTAKE_TOKENS) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -452,7 +443,6 @@ def create_transaction_for_unbonding_nodes( sender: IAccount, keys: str, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -468,7 +458,7 @@ def create_transaction_for_unbonding_nodes( parsed_keys, num_keys = utils.parse_keys(keys) data = f"unBondNodes{parsed_keys}" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNBOND, num_keys) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -496,7 +486,6 @@ def create_transaction_for_unbonding_tokens( sender: IAccount, value: int, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -511,7 +500,7 @@ def create_transaction_for_unbonding_tokens( ) -> Transaction: data = f"unBondTokens@{self.serializer.serialize([BigUIntValue(value)])}" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNBOND_TOKENS) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -538,7 +527,6 @@ def create_transaction_for_cleaning_registered_data( self, sender: IAccount, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -553,7 +541,7 @@ def create_transaction_for_cleaning_registered_data( ) -> Transaction: data = "cleanRegisteredData" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.CLEAN_REGISTERED_DATA) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) @@ -581,7 +569,6 @@ def create_transaction_for_restaking_unstaked_nodes( sender: IAccount, keys: str, native_amount: int, - estimate_gas: bool, gas_limit: int, gas_price: int, nonce: int, @@ -597,7 +584,7 @@ def create_transaction_for_restaking_unstaked_nodes( parsed_keys, num_keys = utils.parse_keys(keys) data = f"reStakeUnStakedNodes{parsed_keys}" - if estimate_gas: + if not gas_limit: gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.RESTAKE_UNSTAKED_NODES, num_keys) receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) From f6c499b0a56f2872c948174ad799011de3d6a331 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 24 Feb 2025 15:49:18 +0200 Subject: [PATCH 52/84] use single index arg for each wallet --- multiversx_sdk_cli/cli_shared.py | 66 +++++---------------- multiversx_sdk_cli/tests/test_cli_wallet.py | 6 +- 2 files changed, 18 insertions(+), 54 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index fbfc26cb..8eeb2173 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -148,12 +148,6 @@ def add_wallet_args(args: list[str], sub: Any): required=False, help="🔑 the PEM file, if keyfile not provided", ) - sub.add_argument( - "--pem-index", - type=int, - default=0, - help="🔑 the index in the PEM file (default: %(default)s)", - ) sub.add_argument( "--keyfile", required=False, @@ -163,12 +157,6 @@ def add_wallet_args(args: list[str], sub: Any): "--passfile", help="🔑 a file containing keyfile's password, if keyfile provided", ) - sub.add_argument( - "--address-index", - type=int, - default=None, - help="🔑 the index of the address in the keyfile; should only be provided for keyfile of kind = mnemonic", - ) sub.add_argument( "--ledger", action="store_true", @@ -177,10 +165,10 @@ def add_wallet_args(args: list[str], sub: Any): help="🔐 bool flag for signing transaction using ledger", ) sub.add_argument( - "--ledger-address-index", + "--index", type=int, default=0, - help="🔐 the index of the address when using Ledger", + help="🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: %(default)s)", ) sub.add_argument("--sender-username", required=False, help="🖄 the username of the sender") @@ -190,12 +178,6 @@ def add_guardian_wallet_args(args: list[str], sub: Any): "--guardian-pem", help="🔑 the PEM file, if keyfile not provided", ) - sub.add_argument( - "--guardian-pem-index", - type=int, - default=0, - help="🔑 the index in the PEM file (default: %(default)s)", - ) sub.add_argument( "--guardian-keyfile", help="🔑 a JSON keyfile, if PEM not provided", @@ -204,12 +186,6 @@ def add_guardian_wallet_args(args: list[str], sub: Any): "--guardian-passfile", help="🔑 a file containing keyfile's password, if keyfile provided", ) - sub.add_argument( - "--guardian-address-index", - type=int, - default=None, - help="🔑 the index of the address in the keyfile; should only be provided for keyfile of kind = mnemonic", - ) sub.add_argument( "--guardian-ledger", action="store_true", @@ -217,32 +193,20 @@ def add_guardian_wallet_args(args: list[str], sub: Any): help="🔐 bool flag for signing transaction using ledger", ) sub.add_argument( - "--guardian-ledger-address-index", + "--guardian-index", type=int, default=0, - help="🔐 the index of the address when using Ledger", + help="🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: %(default)s)", ) def add_relayed_v3_wallet_args(args: list[str], sub: Any): sub.add_argument("--relayer-pem", help="🔑 the PEM file, if keyfile not provided") - sub.add_argument( - "--relayer-pem-index", - type=int, - default=0, - help="🔑 the index in the PEM file (default: %(default)s)", - ) sub.add_argument("--relayer-keyfile", help="🔑 a JSON keyfile, if PEM not provided") sub.add_argument( "--relayer-passfile", help="🔑 a file containing keyfile's password, if keyfile provided", ) - sub.add_argument( - "--relayer-address-index", - type=int, - default=None, - help="🔑 the index of the address in the keyfile; should only be provided for keyfile of kind = mnemonic", - ) sub.add_argument( "--relayer-ledger", action="store_true", @@ -250,10 +214,10 @@ def add_relayed_v3_wallet_args(args: list[str], sub: Any): help="🔐 bool flag for signing transaction using ledger", ) sub.add_argument( - "--relayer-ledger-address-index", + "--relayer-index", type=int, default=0, - help="🔐 the index of the address when using Ledger", + help="🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: %(default)s)", ) @@ -305,17 +269,17 @@ def prepare_account(args: Any): hrp = config.get_address_hrp() if args.pem: - return Account.new_from_pem(file_path=Path(args.pem), index=args.pem_index, hrp=hrp) + return Account.new_from_pem(file_path=Path(args.pem), index=args.index, hrp=hrp) elif args.keyfile: password = load_password(args) return Account.new_from_keystore( file_path=Path(args.keyfile), password=password, - address_index=args.address_index, + address_index=args.index, hrp=hrp, ) elif args.ledger: - return LedgerAccount(address_index=args.ledger_address_index) + return LedgerAccount(address_index=args.index) else: raise errors.NoWalletProvided() @@ -324,17 +288,17 @@ def load_guardian_account(args: Any) -> Union[IAccount, None]: hrp = config.get_address_hrp() if args.guardian_pem: - return Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_pem_index, hrp=hrp) + return Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_index, hrp=hrp) elif args.guardian_keyfile: password = load_guardian_password(args) return Account.new_from_keystore( file_path=Path(args.guardian_keyfile), password=password, - address_index=args.guardian_address_index, + address_index=args.guardian_index, hrp=hrp, ) elif args.guardian_ledger: - return LedgerAccount(address_index=args.guardian_ledger_address_index) + return LedgerAccount(address_index=args.guardian_index) return None @@ -369,17 +333,17 @@ def load_relayer_account(args: Any) -> Union[IAccount, None]: hrp = config.get_address_hrp() if args.relayer_pem: - return Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_pem_index, hrp=hrp) + return Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_index, hrp=hrp) elif args.relayer_keyfile: password = load_relayer_password(args) return Account.new_from_keystore( file_path=Path(args.relayer_keyfile), password=password, - address_index=args.relayer_address_index, + address_index=args.relayer_index, hrp=hrp, ) elif args.relayer_ledger: - return LedgerAccount(address_index=args.relayer_ledger_address_index) + return LedgerAccount(address_index=args.relayer_index) return None diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index 50605851..f5f285dc 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -415,7 +415,7 @@ def test_sign_and_verify_message_with_multi_address_pem(capsys: Any): message, "--pem", str(multi_address_pem_path), - "--pem-index", + "--index", "0", ] ) @@ -454,7 +454,7 @@ def test_sign_and_verify_message_with_multi_address_pem(capsys: Any): message, "--pem", str(multi_address_pem_path), - "--pem-index", + "--index", "1", ] ) @@ -493,7 +493,7 @@ def test_sign_and_verify_message_with_multi_address_pem(capsys: Any): message, "--pem", str(multi_address_pem_path), - "--pem-index", + "--index", "2", ] ) From 7fef80beff7a4bfc4c694072ff94ff8601306245 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 27 Feb 2025 12:52:15 +0200 Subject: [PATCH 53/84] rename args validation functions --- multiversx_sdk_cli/args_validation.py | 20 +++++++++--------- multiversx_sdk_cli/cli_contracts.py | 28 +++++++++++++------------- multiversx_sdk_cli/cli_shared.py | 8 ++++---- multiversx_sdk_cli/cli_transactions.py | 20 +++++++++--------- multiversx_sdk_cli/cli_validators.py | 12 +++++------ multiversx_sdk_cli/dns.py | 12 +++++------ 6 files changed, 50 insertions(+), 50 deletions(-) diff --git a/multiversx_sdk_cli/args_validation.py b/multiversx_sdk_cli/args_validation.py index 56072e70..e035df4a 100644 --- a/multiversx_sdk_cli/args_validation.py +++ b/multiversx_sdk_cli/args_validation.py @@ -3,13 +3,13 @@ from multiversx_sdk_cli.errors import InvalidArgumentsError -def ensure_required_transaction_args_are_provided(args: Any): - _ensure_nonce_args(args) - _ensure_receiver_args(args) - _ensure_gas_limit_args(args) +def validate_transaction_args(args: Any): + _validate_nonce_args(args) + _validate_receiver_args(args) + _validate_gas_limit_args(args) -def _ensure_nonce_args(args: Any): +def _validate_nonce_args(args: Any): """If nonce is not provided, ensure that recall_nonce is provided. If recall_nonce is provided, ensure that proxy is provided.""" if hasattr(args, "nonce") and args.nonce is None: if not args.recall_nonce: @@ -19,13 +19,13 @@ def _ensure_nonce_args(args: Any): raise InvalidArgumentsError("--proxy must be provided if --recall-nonce is used") -def _ensure_receiver_args(args: Any): +def _validate_receiver_args(args: Any): """Ensure that receiver is provided.""" if hasattr(args, "receiver") and not args.receiver: raise InvalidArgumentsError("--receiver must be provided") -def _ensure_gas_limit_args(args: Any): +def _validate_gas_limit_args(args: Any): """Ensure that gas_limit is provided.""" if hasattr(args, "gas_limit") and not args.gas_limit: if hasattr(args, "estimate_gas") and not args.estimate_gas: @@ -54,16 +54,16 @@ def ensure_relayer_wallet_args_are_provided(args: Any): raise InvalidArgumentsError("One of --relayer-pem, --relayer-keyfile, or --relayer-ledger must be provided") -def ensure_broadcast_args(args: Any): +def validate_broadcast_args(args: Any): if args.send and args.simulate: raise InvalidArgumentsError("Cannot both 'simulate' and 'send' a transaction") -def ensure_chain_id_args(args: Any): +def validate_chain_id_args(args: Any): if not args.chain and not args.proxy: raise InvalidArgumentsError("Either --chain or --proxy must be provided") -def ensure_proxy_argument(args: Any): +def validate_proxy_argment(args: Any): if not args.proxy: raise InvalidArgumentsError("--proxy must be provided") diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index f2168da3..5132fe5f 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -15,11 +15,11 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.args_validation import ( - ensure_broadcast_args, - ensure_chain_id_args, - ensure_proxy_argument, - ensure_required_transaction_args_are_provided, ensure_wallet_args_are_provided, + validate_broadcast_args, + validate_chain_id_args, + validate_proxy_argment, + validate_transaction_args, ) from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers @@ -344,10 +344,10 @@ def build(args: Any): def deploy(args: Any): logger.debug("deploy") - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender = cli_shared.prepare_account(args) @@ -404,10 +404,10 @@ def deploy(args: Any): def call(args: Any): logger.debug("call") - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender = cli_shared.prepare_account(args) @@ -457,10 +457,10 @@ def call(args: Any): def upgrade(args: Any): logger.debug("upgrade") - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender = cli_shared.prepare_account(args) @@ -513,7 +513,7 @@ def upgrade(args: Any): def query(args: Any): logger.debug("query") - ensure_proxy_argument(args) + validate_proxy_argment(args) # we don't need chainID to query a contract; we use the provided proxy factory_config = TransactionsFactoryConfig("") diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 4071ffe1..13759193 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -510,10 +510,10 @@ def get_chain_id(chain_id: str, proxy_url: str) -> str: if chain_id: return chain_id - else: - network_provider_config = config.get_config_for_network_providers() - proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) - return proxy.get_network_config().chain_id + + network_provider_config = config.get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) + return proxy.get_network_config().chain_id def add_broadcast_args(sub: Any, simulate: bool = True): diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index ec722457..35e55679 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -13,12 +13,12 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.args_validation import ( - ensure_broadcast_args, - ensure_chain_id_args, - ensure_proxy_argument, ensure_relayer_wallet_args_are_provided, - ensure_required_transaction_args_are_provided, ensure_wallet_args_are_provided, + validate_broadcast_args, + validate_chain_id_args, + validate_proxy_argment, + validate_transaction_args, ) from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.cli_output import CLIOutputBuilder @@ -114,10 +114,10 @@ def _add_common_arguments(args: list[str], sub: Any): def create_transaction(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender = cli_shared.prepare_account(args) @@ -181,7 +181,7 @@ def prepare_token_transfers(transfers: list[Any]) -> list[TokenTransfer]: def send_transaction(args: Any): - ensure_proxy_argument(args) + validate_proxy_argment(args) tx = load_transaction_from_file(args.infile) output = CLIOutputBuilder() @@ -198,7 +198,7 @@ def send_transaction(args: Any): def sign_transaction(args: Any): - ensure_broadcast_args(args) + validate_broadcast_args(args) tx = load_transaction_from_file(args.infile) @@ -234,7 +234,7 @@ def sign_transaction(args: Any): def relay_transaction(args: Any): ensure_relayer_wallet_args_are_provided(args) - ensure_broadcast_args(args) + validate_broadcast_args(args) tx = load_transaction_from_file(args.infile) relayer = cli_shared.prepare_relayer_account(args) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index ecb7ddca..116181ce 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -5,10 +5,10 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.args_validation import ( - ensure_broadcast_args, - ensure_chain_id_args, - ensure_required_transaction_args_are_provided, ensure_wallet_args_are_provided, + validate_broadcast_args, + validate_chain_id_args, + validate_transaction_args, ) from multiversx_sdk_cli.validators.core import ValidatorsController @@ -198,10 +198,10 @@ def do_stake(args: Any): def validate_args(args: Any) -> None: - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) def prepare_sender(args: Any): diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 7b3869c6..3abbfca2 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -10,10 +10,10 @@ from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.args_validation import ( - ensure_broadcast_args, - ensure_chain_id_args, - ensure_required_transaction_args_are_provided, ensure_wallet_args_are_provided, + validate_broadcast_args, + validate_chain_id_args, + validate_transaction_args, ) from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX @@ -61,10 +61,10 @@ def validate_name(name: str, shard_id: int, proxy: INetworkProvider): def register(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender = cli_shared.prepare_account(args) From e7bb9198da008212fe3b0955e8ab4ecd83e120da Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 27 Feb 2025 14:10:40 +0200 Subject: [PATCH 54/84] fixes after merge --- multiversx_sdk_cli/args_validation.py | 4 +- multiversx_sdk_cli/cli_contracts.py | 4 +- multiversx_sdk_cli/cli_delegation.py | 118 ++++++++++++------------- multiversx_sdk_cli/cli_transactions.py | 4 +- 4 files changed, 65 insertions(+), 65 deletions(-) diff --git a/multiversx_sdk_cli/args_validation.py b/multiversx_sdk_cli/args_validation.py index 1a6c1915..8c48da2a 100644 --- a/multiversx_sdk_cli/args_validation.py +++ b/multiversx_sdk_cli/args_validation.py @@ -59,7 +59,7 @@ def validate_broadcast_args(args: Any): raise InvalidArgumentsError("Cannot both 'simulate' and 'send' a transaction") if args.send or args.simulate: - ensure_proxy_argument(args) + validate_proxy_argument(args) def validate_chain_id_args(args: Any): @@ -67,6 +67,6 @@ def validate_chain_id_args(args: Any): raise InvalidArgumentsError("Either --chain or --proxy must be provided") -def validate_proxy_argment(args: Any): +def validate_proxy_argument(args: Any): if not args.proxy: raise InvalidArgumentsError("--proxy must be provided") diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 5132fe5f..b08646ec 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -18,7 +18,7 @@ ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, - validate_proxy_argment, + validate_proxy_argument, validate_transaction_args, ) from multiversx_sdk_cli.cli_output import CLIOutputBuilder @@ -513,7 +513,7 @@ def upgrade(args: Any): def query(args: Any): logger.debug("query") - validate_proxy_argment(args) + validate_proxy_argument(args) # we don't need chainID to query a contract; we use the provided proxy factory_config = TransactionsFactoryConfig("") diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index c0e91ba1..a91664fb 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -11,11 +11,11 @@ from multiversx_sdk_cli import cli_shared, errors, utils from multiversx_sdk_cli.args_validation import ( - ensure_broadcast_args, - ensure_chain_id_args, - ensure_proxy_argument, - ensure_required_transaction_args_are_provided, ensure_wallet_args_are_provided, + validate_broadcast_args, + validate_chain_id_args, + validate_proxy_argument, + validate_transaction_args, ) from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.delegation import DelegationOperations @@ -415,10 +415,10 @@ def prepare_relayer(args: Any): def do_create_delegation_contract(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -452,7 +452,7 @@ def do_create_delegation_contract(args: Any): def get_contract_address_by_deploy_tx_hash(args: Any): - ensure_proxy_argument(args) + validate_proxy_argument(args) config = get_config_for_network_providers() proxy = ProxyNetworkProvider(url=args.proxy, config=config) @@ -469,10 +469,10 @@ def get_contract_address_by_deploy_tx_hash(args: Any): def add_new_nodes(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -527,10 +527,10 @@ def _get_public_keys_and_signed_messages(args: Any) -> tuple[list[ValidatorPubli def remove_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -585,10 +585,10 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def stake_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -632,10 +632,10 @@ def _check_if_either_bls_keys_or_validators_file_are_provided(args: Any): def unbond_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -671,10 +671,10 @@ def unbond_nodes(args: Any): def unstake_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -710,10 +710,10 @@ def unstake_nodes(args: Any): def unjail_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -748,10 +748,10 @@ def unjail_nodes(args: Any): def delegate(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -788,10 +788,10 @@ def delegate(args: Any): def claim_rewards(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -823,10 +823,10 @@ def claim_rewards(args: Any): def redelegate_rewards(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -858,10 +858,10 @@ def redelegate_rewards(args: Any): def undelegate(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -898,10 +898,10 @@ def undelegate(args: Any): def withdraw(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -934,10 +934,10 @@ def withdraw(args: Any): def change_service_fee(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -971,10 +971,10 @@ def change_service_fee(args: Any): def modify_delegation_cap(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -1008,10 +1008,10 @@ def modify_delegation_cap(args: Any): def automatic_activation(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -1046,10 +1046,10 @@ def automatic_activation(args: Any): def redelegate_cap(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -1084,10 +1084,10 @@ def redelegate_cap(args: Any): def set_metadata(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) @@ -1123,10 +1123,10 @@ def set_metadata(args: Any): def make_new_contract_from_validator_data(args: Any): - ensure_required_transaction_args_are_provided(args) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) - ensure_broadcast_args(args) - ensure_chain_id_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) sender, nonce = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 35e55679..05535c2c 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -17,7 +17,7 @@ ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, - validate_proxy_argment, + validate_proxy_argument, validate_transaction_args, ) from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController @@ -181,7 +181,7 @@ def prepare_token_transfers(transfers: list[Any]) -> list[TokenTransfer]: def send_transaction(args: Any): - validate_proxy_argment(args) + validate_proxy_argument(args) tx = load_transaction_from_file(args.infile) output = CLIOutputBuilder() From 1423030b0275e305e1ed8818c838e993b0cb8aba Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 28 Feb 2025 16:20:56 +0200 Subject: [PATCH 55/84] remove check for minimum delegated/undelegated amount --- multiversx_sdk_cli/cli_delegation.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index a91664fb..6e10321f 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -21,8 +21,6 @@ from multiversx_sdk_cli.delegation import DelegationOperations from multiversx_sdk_cli.validators.validators_file import ValidatorsFile -MINIMUM_AMOUNT_TO_DELEGATE = 1_000_000_000_000_000_000 # 1 EGLD - def setup_parser(args: list[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser(subparsers, "staking-provider", "Staking provider omnitool") @@ -758,10 +756,7 @@ def delegate(args: Any): relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit - value = int(args.value) - if value < MINIMUM_AMOUNT_TO_DELEGATE: - errors.BadUserInput(f"Value must be greater than {MINIMUM_AMOUNT_TO_DELEGATE} (1 EGLD)") chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -868,10 +863,7 @@ def undelegate(args: Any): relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit - value = int(args.value) - if value < MINIMUM_AMOUNT_TO_DELEGATE: - errors.BadUserInput(f"Value must be greater than {MINIMUM_AMOUNT_TO_DELEGATE} (1 EGLD)") chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) From b5696ca006fc72310520b6e5024317c97702bb8b Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 3 Mar 2025 14:12:43 +0200 Subject: [PATCH 56/84] fixes after review --- multiversx_sdk_cli/cli_delegation.py | 273 +++++++++------------------ 1 file changed, 86 insertions(+), 187 deletions(-) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 6e10321f..8f5bf1c1 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -383,21 +383,21 @@ def _add_common_arguments(args: list[str], sub: Any): cli_shared.add_relayed_v3_wallet_args(args, sub) -def ensure_arguments_are_provided_and_prepared(args: Any): - cli_shared.check_guardian_args(args) - cli_shared.check_broadcast_args(args) - cli_shared.prepare_chain_id_in_args(args) - cli_shared.prepare_nonce_in_args(args) +def validate_arguments(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) def prepare_sender(args: Any): sender = cli_shared.prepare_account(args) - nonce = ( + sender.nonce = ( int(args.nonce) if args.nonce is not None else cli_shared.get_current_nonce_for_address(sender.address, args.proxy) ) - return sender, nonce + return sender def prepare_guardian(args: Any): @@ -412,21 +412,22 @@ def prepare_relayer(args: Any): return relayer, relayer_address +def _get_delegation_controller(args: Any): + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + config = TransactionsFactoryConfig(chain_id) + delegation = DelegationOperations(config) + return delegation + + def do_create_delegation_contract(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = args.gas_limit if args.gas_limit else 0 + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_new_delegation_contract( owner=sender, @@ -435,7 +436,7 @@ def do_create_delegation_contract(args: Any): service_fee=int(args.service_fee), gas_limit=gas_limit, gas_price=int(args.gas_price), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -467,22 +468,16 @@ def get_contract_address_by_deploy_tx_hash(args: Any): def add_new_nodes(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit public_keys, signed_messages = _get_public_keys_and_signed_messages(args) + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_adding_nodes( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -491,7 +486,7 @@ def add_new_nodes(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -525,22 +520,16 @@ def _get_public_keys_and_signed_messages(args: Any) -> tuple[list[ValidatorPubli def remove_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit public_keys = _load_validators_public_keys(args) + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_removing_nodes( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -548,7 +537,7 @@ def remove_nodes(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -583,22 +572,16 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def stake_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit public_keys = _load_validators_public_keys(args) + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_staking_nodes( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -606,7 +589,7 @@ def stake_nodes(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -630,22 +613,16 @@ def _check_if_either_bls_keys_or_validators_file_are_provided(args: Any): def unbond_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit public_keys = _load_validators_public_keys(args) + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_unbonding_nodes( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -653,7 +630,7 @@ def unbond_nodes(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -669,22 +646,16 @@ def unbond_nodes(args: Any): def unstake_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit public_keys = _load_validators_public_keys(args) + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_unstaking_nodes( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -692,7 +663,7 @@ def unstake_nodes(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -708,22 +679,16 @@ def unstake_nodes(args: Any): def unjail_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit public_keys = _load_validators_public_keys(args) + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_unjailing_nodes( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -731,7 +696,7 @@ def unjail_nodes(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -746,29 +711,23 @@ def unjail_nodes(args: Any): def delegate(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit value = int(args.value) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_delegating( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), gas_limit=gas_limit, gas_price=int(args.gas_price), value=value, - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -783,28 +742,22 @@ def delegate(args: Any): def claim_rewards(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_claiming_rewards( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -818,28 +771,22 @@ def claim_rewards(args: Any): def redelegate_rewards(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = 0 if args.estimate_gas else args.gas_limit + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_redelegating_rewards( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -853,29 +800,23 @@ def redelegate_rewards(args: Any): def undelegate(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit value = int(args.value) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_undelegating( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), gas_limit=gas_limit, gas_price=int(args.gas_price), value=value, - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -890,28 +831,22 @@ def undelegate(args: Any): def withdraw(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_withdrawing( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -926,21 +861,15 @@ def withdraw(args: Any): def change_service_fee(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_changing_service_fee( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -948,7 +877,7 @@ def change_service_fee(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -963,21 +892,15 @@ def change_service_fee(args: Any): def modify_delegation_cap(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_modifying_delegation_cap( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -985,7 +908,7 @@ def modify_delegation_cap(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -1000,21 +923,15 @@ def modify_delegation_cap(args: Any): def automatic_activation(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_automatic_activation( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -1023,7 +940,7 @@ def automatic_activation(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -1038,21 +955,15 @@ def automatic_activation(args: Any): def redelegate_cap(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_redelegate_cap( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -1061,7 +972,7 @@ def redelegate_cap(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -1076,21 +987,15 @@ def redelegate_cap(args: Any): def set_metadata(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_setting_metadata( owner=sender, delegation_contract=Address.new_from_bech32(args.delegation_contract), @@ -1100,7 +1005,7 @@ def set_metadata(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, @@ -1115,21 +1020,15 @@ def set_metadata(args: Any): def make_new_contract_from_validator_data(args: Any): - validate_transaction_args(args) - ensure_wallet_args_are_provided(args) - validate_broadcast_args(args) - validate_chain_id_args(args) + validate_arguments(args) - sender, nonce = prepare_sender(args) + sender = prepare_sender(args) guardian, guardian_address = prepare_guardian(args) relayer, relayer_address = prepare_relayer(args) gas_limit = 0 if args.estimate_gas else args.gas_limit - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - + delegation = _get_delegation_controller(args) tx = delegation.prepare_transaction_for_creating_delegation_contract_from_validator( owner=sender, max_cap=args.max_cap, @@ -1137,7 +1036,7 @@ def make_new_contract_from_validator_data(args: Any): gas_limit=gas_limit, gas_price=int(args.gas_price), value=int(args.value), - nonce=nonce, + nonce=sender.nonce, version=int(args.version), options=int(args.options), guardian_account=guardian, From 06d932616df1d81301d6ed6f971252c1abd35329 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 4 Mar 2025 10:14:24 +0200 Subject: [PATCH 57/84] fixes after review --- multiversx_sdk_cli/cli_delegation.py | 8 -------- multiversx_sdk_cli/cli_shared.py | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 7e448ecc..977ec53b 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -750,10 +750,6 @@ def claim_rewards(args: Any): guardian, guardian_address = cli_shared.prepare_guardian(args) relayer, relayer_address = cli_shared.prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = args.gas_limit if args.gas_limit else 0 delegation = _get_delegation_controller(args) @@ -783,10 +779,6 @@ def redelegate_rewards(args: Any): guardian, guardian_address = cli_shared.prepare_guardian(args) relayer, relayer_address = cli_shared.prepare_relayer(args) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - config = TransactionsFactoryConfig(chain_id) - delegation = DelegationOperations(config) - gas_limit = args.gas_limit if args.gas_limit else 0 delegation = _get_delegation_controller(args) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index e2cafa81..c1a4916c 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -472,7 +472,7 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL def prepare_sender(args: Any): - """Reurns a tuple containing the sender's account and the account nonce. + """Returns the sender's account. If no account was provided, will raise an exception.""" sender = prepare_account(args) sender.nonce = ( From 2bf7b757eeea275e88abd4747cd44b9880782671 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 4 Mar 2025 20:24:35 +0200 Subject: [PATCH 58/84] rename wallet index arguments --- multiversx_sdk_cli/cli_shared.py | 24 ++++++++++----------- multiversx_sdk_cli/tests/test_cli_wallet.py | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 5245dfeb..890c36b2 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -165,7 +165,7 @@ def add_wallet_args(args: list[str], sub: Any): help="🔐 bool flag for signing transaction using ledger", ) sub.add_argument( - "--index", + "--sender-wallet-index", type=int, default=0, help="🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: %(default)s)", @@ -193,7 +193,7 @@ def add_guardian_wallet_args(args: list[str], sub: Any): help="🔐 bool flag for signing transaction using ledger", ) sub.add_argument( - "--guardian-index", + "--guardian-wallet-index", type=int, default=0, help="🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: %(default)s)", @@ -214,7 +214,7 @@ def add_relayed_v3_wallet_args(args: list[str], sub: Any): help="🔐 bool flag for signing transaction using ledger", ) sub.add_argument( - "--relayer-index", + "--relayer-wallet-index", type=int, default=0, help="🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: %(default)s)", @@ -269,17 +269,17 @@ def prepare_account(args: Any): hrp = config.get_address_hrp() if args.pem: - return Account.new_from_pem(file_path=Path(args.pem), index=args.index, hrp=hrp) + return Account.new_from_pem(file_path=Path(args.pem), index=args.sender_wallet_index, hrp=hrp) elif args.keyfile: password = load_password(args) return Account.new_from_keystore( file_path=Path(args.keyfile), password=password, - address_index=args.index, + address_index=args.sender_wallet_index, hrp=hrp, ) elif args.ledger: - return LedgerAccount(address_index=args.index) + return LedgerAccount(address_index=args.sender_wallet_index) else: raise errors.NoWalletProvided() @@ -288,17 +288,17 @@ def load_guardian_account(args: Any) -> Union[IAccount, None]: hrp = config.get_address_hrp() if args.guardian_pem: - return Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_index, hrp=hrp) + return Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_wallet_index, hrp=hrp) elif args.guardian_keyfile: password = load_guardian_password(args) return Account.new_from_keystore( file_path=Path(args.guardian_keyfile), password=password, - address_index=args.guardian_index, + address_index=args.guardian_wallet_index, hrp=hrp, ) elif args.guardian_ledger: - return LedgerAccount(address_index=args.guardian_index) + return LedgerAccount(address_index=args.guardian_wallet_index) return None @@ -333,17 +333,17 @@ def load_relayer_account(args: Any) -> Union[IAccount, None]: hrp = config.get_address_hrp() if args.relayer_pem: - return Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_index, hrp=hrp) + return Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_wallet_index, hrp=hrp) elif args.relayer_keyfile: password = load_relayer_password(args) return Account.new_from_keystore( file_path=Path(args.relayer_keyfile), password=password, - address_index=args.relayer_index, + address_index=args.relayer_wallet_index, hrp=hrp, ) elif args.relayer_ledger: - return LedgerAccount(address_index=args.relayer_index) + return LedgerAccount(address_index=args.relayer_wallet_index) return None diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index f5f285dc..d938add0 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -415,7 +415,7 @@ def test_sign_and_verify_message_with_multi_address_pem(capsys: Any): message, "--pem", str(multi_address_pem_path), - "--index", + "--sender-wallet-index", "0", ] ) @@ -454,7 +454,7 @@ def test_sign_and_verify_message_with_multi_address_pem(capsys: Any): message, "--pem", str(multi_address_pem_path), - "--index", + "--sender-wallet-index", "1", ] ) @@ -493,7 +493,7 @@ def test_sign_and_verify_message_with_multi_address_pem(capsys: Any): message, "--pem", str(multi_address_pem_path), - "--index", + "--sender-wallet-index", "2", ] ) From 42762c7c8ae2ae2d8335e246e35218ad528565cd Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 6 Mar 2025 15:10:33 +0200 Subject: [PATCH 59/84] refactor to use validators transactions factory --- multiversx_sdk_cli/cli_delegation.py | 24 +- multiversx_sdk_cli/cli_validators.py | 68 +- multiversx_sdk_cli/constants.py | 1 - .../tests/test_cli_staking_provider.py | 6 +- .../tests/test_cli_validators.py | 8 +- multiversx_sdk_cli/tests/test_converters.py | 19 - .../tests/test_validators_file.py | 20 - .../tests/testdata/validators.json | 13 - .../tests/testdata/validators.pem | 12 + .../tests/testdata/validators_ci.json | 13 - .../tests/testdata/validators_ci.pem | 12 + .../tests/testdata/validators_file.json | 10 - .../tests/testdata/validators_file.pem | 8 + multiversx_sdk_cli/utils.py | 71 +- multiversx_sdk_cli/validators.py | 575 ++++++++++++++++ multiversx_sdk_cli/validators/__init__.py | 0 multiversx_sdk_cli/validators/core.py | 614 ------------------ .../validators/validators_file.py | 56 -- 18 files changed, 680 insertions(+), 850 deletions(-) delete mode 100644 multiversx_sdk_cli/tests/test_converters.py delete mode 100644 multiversx_sdk_cli/tests/test_validators_file.py delete mode 100644 multiversx_sdk_cli/tests/testdata/validators.json create mode 100644 multiversx_sdk_cli/tests/testdata/validators.pem delete mode 100644 multiversx_sdk_cli/tests/testdata/validators_ci.json create mode 100644 multiversx_sdk_cli/tests/testdata/validators_ci.pem delete mode 100644 multiversx_sdk_cli/tests/testdata/validators_file.json create mode 100644 multiversx_sdk_cli/tests/testdata/validators_file.pem create mode 100644 multiversx_sdk_cli/validators.py delete mode 100644 multiversx_sdk_cli/validators/__init__.py delete mode 100644 multiversx_sdk_cli/validators/core.py delete mode 100644 multiversx_sdk_cli/validators/validators_file.py diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 977ec53b..d1907569 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -7,6 +7,7 @@ ProxyNetworkProvider, TransactionsFactoryConfig, ValidatorPublicKey, + ValidatorsSigners, ) from multiversx_sdk_cli import cli_shared, errors, utils @@ -20,7 +21,6 @@ ) from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.delegation import DelegationOperations -from multiversx_sdk_cli.validators.validators_file import ValidatorsFile def setup_parser(args: list[str], subparsers: Any) -> Any: @@ -63,7 +63,9 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "add-nodes", "Add new nodes must be called by the contract owner", ) - sub.add_argument("--validators-file", required=True, type=str, help="a JSON file describing the Nodes") + sub.add_argument( + "--validators-file", required=True, type=str, help="a PEM file describing the Nodes; can contain multiple nodes" + ) sub.add_argument( "--delegation-contract", required=True, @@ -81,7 +83,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "Remove nodes must be called by the contract owner", ) sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") - sub.add_argument("--validators-file", help="a JSON file describing the Nodes") + sub.add_argument("--validators-file", type=str, help="a PEM file describing the Nodes; can contain multiple nodes") sub.add_argument( "--delegation-contract", required=True, @@ -99,7 +101,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "Stake nodes must be called by the contract owner", ) sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") - sub.add_argument("--validators-file", help="a JSON file describing the Nodes") + sub.add_argument("--validators-file", type=str, help="a PEM file describing the Nodes; can contain multiple nodes") sub.add_argument( "--delegation-contract", required=True, @@ -117,7 +119,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "Unbond nodes must be called by the contract owner", ) sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") - sub.add_argument("--validators-file", help="a JSON file describing the Nodes") + sub.add_argument("--validators-file", type=str, help="a PEM file describing the Nodes; can contain multiple nodes") sub.add_argument( "--delegation-contract", required=True, @@ -134,7 +136,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "Unstake nodes must be called by the contract owner", ) sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") - sub.add_argument("--validators-file", help="a JSON file describing the Nodes") + sub.add_argument("--validators-file", type=str, help="a PEM file describing the Nodes; can contain multiple nodes") sub.add_argument( "--delegation-contract", required=True, @@ -151,7 +153,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "Unjail nodes must be called by the contract owner", ) sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") - sub.add_argument("--validators-file", help="a JSON file describing the Nodes") + sub.add_argument("--validators-file", type=str, help="a PEM file describing the Nodes; can contain multiple nodes") sub.add_argument( "--delegation-contract", required=True, @@ -504,8 +506,8 @@ def add_new_nodes(args: Any): def _get_public_keys_and_signed_messages(args: Any) -> tuple[list[ValidatorPublicKey], list[bytes]]: validators_file_path = Path(args.validators_file).expanduser() - validators_file = ValidatorsFile(validators_file_path) - signers = validators_file.load_signers() + validators_file = ValidatorsSigners.new_from_pem(validators_file_path) + signers = validators_file.get_signers() pubkey = Address.new_from_bech32(args.delegation_contract).get_public_key() @@ -558,8 +560,8 @@ def _load_validators_public_keys(args: Any) -> list[ValidatorPublicKey]: return _parse_public_bls_keys(args.bls_keys) validators_file_path = Path(args.validators_file).expanduser() - validators_file = ValidatorsFile(validators_file_path) - return validators_file.load_public_keys() + validators_file = ValidatorsSigners.new_from_pem(validators_file_path) + return validators_file.get_public_keys() def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 0e88b6f9..f6bc44f7 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -1,7 +1,7 @@ from pathlib import Path from typing import Any -from multiversx_sdk import Address +from multiversx_sdk import Address, ValidatorPublicKey, ValidatorsSigners from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.args_validation import ( @@ -11,7 +11,7 @@ validate_nonce_args, validate_receiver_args, ) -from multiversx_sdk_cli.validators.core import ValidatorsController +from multiversx_sdk_cli.validators import ValidatorsController def setup_parser(args: list[str], subparsers: Any) -> Any: @@ -28,7 +28,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--validators-file", required=not (utils.is_arg_present(args, "--top-up")), - help="a JSON file describing the Nodes", + help="a PEM file describing the nodes; can contain multiple nodes", ) sub.add_argument( "--top-up", @@ -163,7 +163,7 @@ def do_stake(args: Any): gas_limit = args.gas_limit if args.gas_limit else 0 rewards_address = Address.new_from_bech32(args.reward_address) if args.reward_address else None - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) if args.top_up: tx = controller.create_transaction_for_topping_up( @@ -182,10 +182,10 @@ def do_stake(args: Any): guardian_2fa_code=args.guardian_2fa_code, ) else: - validators_file = Path(args.validators_file) + validators_file = _load_validators_signers(args.validators_file) tx = controller.create_transaction_for_staking( sender=sender, - validators_file=validators_file, + validators=validators_file, native_amount=native_amount, gas_limit=gas_limit, gas_price=args.gas_price, @@ -204,6 +204,28 @@ def do_stake(args: Any): cli_shared.send_or_simulate(tx, args) +def _get_validators_controller(args: Any): + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + validators = ValidatorsController(chain_id) + return validators + + +def _load_validators_signers(validators_pem: str) -> ValidatorsSigners: + validators_file_path = Path(validators_pem).expanduser() + validators_file = ValidatorsSigners.new_from_pem(validators_file_path) + return validators_file + + +def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: + keys = public_bls_keys.split(",") + validator_public_keys: list[ValidatorPublicKey] = [] + + for key in keys: + validator_public_keys.append(ValidatorPublicKey(bytes.fromhex(key))) + + return validator_public_keys + + def do_unstake(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) @@ -212,9 +234,9 @@ def do_unstake(args: Any): native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 - keys = args.nodes_public_keys + keys = _parse_public_bls_keys(args.nodes_public_keys) - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_unstaking( sender=sender, keys=keys, @@ -243,9 +265,9 @@ def do_unjail(args: Any): native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 - keys = args.nodes_public_keys + keys = _parse_public_bls_keys(args.nodes_public_keys) - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_unjailing( sender=sender, keys=keys, @@ -274,9 +296,9 @@ def do_unbond(args: Any): native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 - keys = args.nodes_public_keys + keys = _parse_public_bls_keys(args.nodes_public_keys) - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_unbonding( sender=sender, keys=keys, @@ -307,7 +329,7 @@ def change_reward_address(args: Any): gas_limit = args.gas_limit if args.gas_limit else 0 rewards_address = Address.new_from_bech32(args.reward_address) - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_changing_rewards_address( sender=sender, rewards_address=rewards_address, @@ -337,7 +359,7 @@ def do_claim(args: Any): native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_claiming( sender=sender, native_amount=native_amount, @@ -366,9 +388,9 @@ def do_unstake_nodes(args: Any): native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 - keys = args.nodes_public_keys + keys = _parse_public_bls_keys(args.nodes_public_keys) - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_unstaking_nodes( sender=sender, keys=keys, @@ -399,7 +421,7 @@ def do_unstake_tokens(args: Any): value = int(args.unstake_value) gas_limit = args.gas_limit if args.gas_limit else 0 - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_unstaking_tokens( sender=sender, value=value, @@ -429,9 +451,9 @@ def do_unbond_nodes(args: Any): native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 - keys = args.nodes_public_keys + keys = _parse_public_bls_keys(args.nodes_public_keys) - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_unbonding_nodes( sender=sender, keys=keys, @@ -462,7 +484,7 @@ def do_unbond_tokens(args: Any): value = int(args.unbond_value) gas_limit = args.gas_limit if args.gas_limit else 0 - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_unbonding_tokens( sender=sender, value=value, @@ -492,7 +514,7 @@ def do_clean_registered_data(args: Any): native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_cleaning_registered_data( sender=sender, native_amount=native_amount, @@ -520,9 +542,9 @@ def do_restake_unstaked_nodes(args: Any): native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 - keys = args.nodes_public_keys + keys = _parse_public_bls_keys(args.nodes_public_keys) - controller = ValidatorsController(args.chain) + controller = _get_validators_controller(args) tx = controller.create_transaction_for_restaking_unstaked_nodes( sender=sender, keys=keys, diff --git a/multiversx_sdk_cli/constants.py b/multiversx_sdk_cli/constants.py index ce1b5e5d..556611ac 100644 --- a/multiversx_sdk_cli/constants.py +++ b/multiversx_sdk_cli/constants.py @@ -14,5 +14,4 @@ NUMBER_OF_SHARDS = 3 DEFAULT_GAS_PRICE = 1000000000 -GAS_PER_DATA_BYTE = 1500 MIN_GAS_LIMIT = 50000 diff --git a/multiversx_sdk_cli/tests/test_cli_staking_provider.py b/multiversx_sdk_cli/tests/test_cli_staking_provider.py index 7a280bf5..a0fc6c47 100644 --- a/multiversx_sdk_cli/tests/test_cli_staking_provider.py +++ b/multiversx_sdk_cli/tests/test_cli_staking_provider.py @@ -9,7 +9,7 @@ first_bls_key = "f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d" second_bls_key = "1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d" -validators_file = parent / "testdata" / "validators_file.json" +validators_file = parent / "testdata" / "validators_file.pem" def test_create_new_delegation_contract(capsys: Any): @@ -85,7 +85,7 @@ def test_create_new_delegation_contract_with_provided_gas_limit(capsys: Any): def test_add_nodes(capsys: Any): - validators_file = parent / "testdata" / "validators.json" + validators_file = parent / "testdata" / "validators.pem" main( [ @@ -121,7 +121,7 @@ def test_add_nodes(capsys: Any): def test_add_nodes_with_gas_limit(capsys: Any): - validators_file = parent / "testdata" / "validators.json" + validators_file = parent / "testdata" / "validators.pem" main( [ diff --git a/multiversx_sdk_cli/tests/test_cli_validators.py b/multiversx_sdk_cli/tests/test_cli_validators.py index f7f85da8..9187cb3d 100644 --- a/multiversx_sdk_cli/tests/test_cli_validators.py +++ b/multiversx_sdk_cli/tests/test_cli_validators.py @@ -16,7 +16,7 @@ def test_stake(capsys: Any): - validators_json = testdata_path / "validators_file.json" + validators_pem = testdata_path / "validators_file.pem" return_code = main( [ @@ -27,7 +27,7 @@ def test_stake(capsys: Any): "--value", "2500000000000000000000", "--validators-file", - str(validators_json), + str(validators_pem), "--reward-address", reward_address, "--chain", @@ -98,7 +98,7 @@ def test_top_up(capsys: Any): def test_stake_with_relayer_and_guardian(capsys: Any): - validators_json = testdata_path / "validators_file.json" + validators_pem = testdata_path / "validators_file.pem" return_code = main( [ @@ -109,7 +109,7 @@ def test_stake_with_relayer_and_guardian(capsys: Any): "--value", "2500000000000000000000", "--validators-file", - str(validators_json), + str(validators_pem), "--reward-address", reward_address, "--chain", diff --git a/multiversx_sdk_cli/tests/test_converters.py b/multiversx_sdk_cli/tests/test_converters.py deleted file mode 100644 index 0c6d62fe..00000000 --- a/multiversx_sdk_cli/tests/test_converters.py +++ /dev/null @@ -1,19 +0,0 @@ -from multiversx_sdk_cli import utils - - -def test_str_to_hex_str(): - my_str = "1000000" - hex_str = utils.str_int_to_hex_str(my_str) - assert hex_str == "0f4240" - - my_str = "100000000000000000" - hex_str = utils.str_int_to_hex_str(my_str) - assert hex_str == "00016345785d8a0000" - - -def test_parse_keys(): - keys = "myKey,newKey,anotherKey" - parsed_keys, num_keys = utils.parse_keys(keys) - - assert num_keys == 3 - assert parsed_keys == "@myKey@newKey@anotherKey" diff --git a/multiversx_sdk_cli/tests/test_validators_file.py b/multiversx_sdk_cli/tests/test_validators_file.py deleted file mode 100644 index 0015a741..00000000 --- a/multiversx_sdk_cli/tests/test_validators_file.py +++ /dev/null @@ -1,20 +0,0 @@ -from pathlib import Path - -from multiversx_sdk_cli.validators.validators_file import ValidatorsFile - - -class ValidatorsFileTestCase: - testdata = Path(__file__).parent.joinpath("testdata") - validators_file_path = testdata / "validators.json" - - def test_read_validators_files_num_of_nodes(self): - validators_file = ValidatorsFile(self.validators_file_path) - - num_of_nodes = validators_file.get_num_of_nodes() - assert num_of_nodes == 3 - - def test_read_validators_files_get_validators_list(self): - validators_file = ValidatorsFile(self.validators_file_path) - - validators_list = validators_file.get_validators_list() - assert len(validators_list) == 3 diff --git a/multiversx_sdk_cli/tests/testdata/validators.json b/multiversx_sdk_cli/tests/testdata/validators.json deleted file mode 100644 index 96f6315f..00000000 --- a/multiversx_sdk_cli/tests/testdata/validators.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "validators": [ - { - "pemFile": "~/multiversx-sdk/testwallets/latest/validators/validatorKey00.pem" - }, - { - "pemFile": "~/multiversx-sdk/testwallets/latest/validators/validatorKey01.pem" - }, - { - "pemFile": "~/multiversx-sdk/testwallets/latest/validators/validatorKey02.pem" - } - ] -} diff --git a/multiversx_sdk_cli/tests/testdata/validators.pem b/multiversx_sdk_cli/tests/testdata/validators.pem new file mode 100644 index 00000000..3e598c61 --- /dev/null +++ b/multiversx_sdk_cli/tests/testdata/validators.pem @@ -0,0 +1,12 @@ +-----BEGIN PRIVATE KEY for e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208----- +N2NmZjk5YmQ2NzE1MDJkYjdkMTViYzhhYmMwYzlhODA0ZmI5MjU0MDZmYmRkNTBm +MWU0YzE3YTRjZDc3NDI0Nw== +-----END PRIVATE KEY for e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208----- +-----BEGIN PRIVATE KEY for 78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d----- +ODA4NWJhMWQ3ZjdjM2RiOTM4YWQ3MDU5NWEyYmRhYjA5NjQ0ZjFlYzM4MDNiZTE3 +MWMzM2YxNGJjODBkNGUzYg== +-----END PRIVATE KEY for 78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d----- +-----BEGIN PRIVATE KEY for 7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491----- +ZjFkYjBkMjE4NmJiZDEzOGQ4MTk5MzM3MDJlMmMxMmFkNDk5MTI0YzQ3N2Q0OGM3 +ZDM3MTM2N2E3MWZmMmM1Zg== +-----END PRIVATE KEY for 7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491----- diff --git a/multiversx_sdk_cli/tests/testdata/validators_ci.json b/multiversx_sdk_cli/tests/testdata/validators_ci.json deleted file mode 100644 index 99a390c6..00000000 --- a/multiversx_sdk_cli/tests/testdata/validators_ci.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "validators": [ - { - "pemFile": "~/work/mx-sdk-py-cli/mx-sdk-py-cli/localnet/validator00/config/validatorKey.pem" - }, - { - "pemFile": "~/work/mx-sdk-py-cli/mx-sdk-py-cli/localnet/validator01/config/validatorKey.pem" - }, - { - "pemFile": "~/work/mx-sdk-py-cli/mx-sdk-py-cli/localnet/validator02/config/validatorKey.pem" - } - ] -} diff --git a/multiversx_sdk_cli/tests/testdata/validators_ci.pem b/multiversx_sdk_cli/tests/testdata/validators_ci.pem new file mode 100644 index 00000000..3e598c61 --- /dev/null +++ b/multiversx_sdk_cli/tests/testdata/validators_ci.pem @@ -0,0 +1,12 @@ +-----BEGIN PRIVATE KEY for e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208----- +N2NmZjk5YmQ2NzE1MDJkYjdkMTViYzhhYmMwYzlhODA0ZmI5MjU0MDZmYmRkNTBm +MWU0YzE3YTRjZDc3NDI0Nw== +-----END PRIVATE KEY for e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208----- +-----BEGIN PRIVATE KEY for 78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d----- +ODA4NWJhMWQ3ZjdjM2RiOTM4YWQ3MDU5NWEyYmRhYjA5NjQ0ZjFlYzM4MDNiZTE3 +MWMzM2YxNGJjODBkNGUzYg== +-----END PRIVATE KEY for 78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d----- +-----BEGIN PRIVATE KEY for 7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491----- +ZjFkYjBkMjE4NmJiZDEzOGQ4MTk5MzM3MDJlMmMxMmFkNDk5MTI0YzQ3N2Q0OGM3 +ZDM3MTM2N2E3MWZmMmM1Zg== +-----END PRIVATE KEY for 7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491----- diff --git a/multiversx_sdk_cli/tests/testdata/validators_file.json b/multiversx_sdk_cli/tests/testdata/validators_file.json deleted file mode 100644 index f9fedd19..00000000 --- a/multiversx_sdk_cli/tests/testdata/validators_file.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "validators": [ - { - "pemFile": "validator_01.pem" - }, - { - "pemFile": "validator_02.pem" - } - ] -} diff --git a/multiversx_sdk_cli/tests/testdata/validators_file.pem b/multiversx_sdk_cli/tests/testdata/validators_file.pem new file mode 100644 index 00000000..14bc776d --- /dev/null +++ b/multiversx_sdk_cli/tests/testdata/validators_file.pem @@ -0,0 +1,8 @@ +-----BEGIN PRIVATE KEY for f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d----- +N2MxOWJmM2EwYzU3Y2RkMWZiMDhlNDYwN2NlYmFhMzY0N2Q2YjkyNjFiNDY5M2Y2 +MWU5NmU1NGIyMThkNDQyYQ== +-----END PRIVATE KEY for f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d----- +-----BEGIN PRIVATE KEY for 1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d----- +MzAzNGIxZDU4NjI4YTg0Mjk4NGRhMGM3MGRhMGI1YTI1MWViYjJhZWJmNTFhZmM1 +YjU4NmUyODM5YjVlNTI2Mw== +-----END PRIVATE KEY for 1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d----- diff --git a/multiversx_sdk_cli/utils.py b/multiversx_sdk_cli/utils.py index c40eeba7..183d4cd7 100644 --- a/multiversx_sdk_cli/utils.py +++ b/multiversx_sdk_cli/utils.py @@ -3,12 +3,11 @@ import os import pathlib import shutil -import stat import sys import tarfile import zipfile from pathlib import Path -from typing import Any, Dict, List, Optional, Protocol, Union, runtime_checkable +from typing import Any, Optional, Protocol, Union, runtime_checkable import toml @@ -19,7 +18,7 @@ @runtime_checkable class ISerializable(Protocol): - def to_dictionary(self) -> Dict[str, Any]: + def to_dictionary(self) -> dict[str, Any]: return self.__dict__ @@ -44,7 +43,7 @@ def default(self, o: Any) -> Any: return super().default(o) -def omit_fields(data: Any, fields: List[str] = []) -> dict[str, Any]: +def omit_fields(data: Any, fields: list[str] = []) -> dict[str, Any]: if isinstance(data, dict): for field in fields: data.pop(field, None) @@ -77,7 +76,7 @@ def ensure_folder(folder: Union[str, Path]): pathlib.Path(folder).mkdir(parents=True, exist_ok=True) -def read_lines(file: Path) -> List[str]: +def read_lines(file: Path) -> list[str]: with open(file) as f: lines = f.readlines() lines = [line.strip() for line in lines] @@ -85,22 +84,6 @@ def read_lines(file: Path) -> List[str]: return lines -def read_binary_file(path: Path) -> bytes: - try: - with open(path, "rb") as binary_file: - return binary_file.read() - except Exception as err: - raise errors.BadFile(str(path), err) from None - - -def read_text_file(path: Path) -> str: - try: - with open(path, "r") as text_file: - return text_file.read() - except Exception as err: - raise errors.BadFile(str(path), err) from None - - def write_file(file_path: Path, text: str): with open(file_path, "w") as file: return file.write(text) @@ -133,24 +116,13 @@ def dump_out_json(data: Any, outfile: Any = None): outfile.write("\n") -def prettify_json_file(filename: str): - data = read_json_file(filename) - write_json_file(filename, data) - - -def get_subfolders(folder: Path) -> List[str]: +def get_subfolders(folder: Path) -> list[str]: return [item.name for item in os.scandir(folder) if item.is_dir() and not item.name.startswith(".")] -def mark_executable(file: str) -> None: - logger.debug(f"Mark [{file}] as executable") - st = os.stat(file) - os.chmod(file, st.st_mode | stat.S_IEXEC) - - -def list_files(folder: Path, suffix: Optional[str] = None) -> List[Path]: +def list_files(folder: Path, suffix: Optional[str] = None) -> list[Path]: folder = folder.expanduser() - files: List[Path] = [folder / file for file in os.listdir(folder)] + files: list[Path] = [folder / file for file in os.listdir(folder)] files = [file for file in files if file.is_file()] if suffix: @@ -169,16 +141,7 @@ def symlink(real: str, link: str) -> None: os.symlink(real, link) -def as_object(data: Object) -> Object: - if isinstance(data, dict): - result = Object() - result.__dict__.update(data) - return result - - return data - - -def is_arg_present(args: List[str], key: str) -> bool: +def is_arg_present(args: list[str], key: str) -> bool: for arg in args: if arg.find("--data") != -1: continue @@ -188,24 +151,6 @@ def is_arg_present(args: List[str], key: str) -> bool: return False -def str_int_to_hex_str(number_str: str) -> str: - num_of_bytes = 1 - if len(number_str) > 2: - num_of_bytes = int(len(number_str) / 2) - int_str = int(number_str) - int_bytes = int_str.to_bytes(num_of_bytes, byteorder="big") - bytes_str = int_bytes.hex() - return bytes_str - - -def parse_keys(bls_public_keys: str): - keys = bls_public_keys.split(",") - parsed_keys = "" - for key in keys: - parsed_keys += "@" + key - return parsed_keys, len(keys) - - def log_explorer(chain: str, name: str, path: str, details: str): networks = { "1": ("MultiversX Mainnet Explorer", "https://explorer.multiversx.com"), diff --git a/multiversx_sdk_cli/validators.py b/multiversx_sdk_cli/validators.py new file mode 100644 index 00000000..d5729621 --- /dev/null +++ b/multiversx_sdk_cli/validators.py @@ -0,0 +1,575 @@ +from typing import Optional + +from multiversx_sdk import ( + Address, + Transaction, + TransactionsFactoryConfig, + ValidatorPublicKey, + ValidatorsSigners, + ValidatorsTransactionsFactory, +) + +from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController +from multiversx_sdk_cli.interfaces import IAccount +from multiversx_sdk_cli.transactions import TransactionsController + + +class ValidatorsController(BaseTransactionsController): + def __init__(self, chain_id: str) -> None: + self.transactions_controller = TransactionsController(chain_id) + self.factory = ValidatorsTransactionsFactory(TransactionsFactoryConfig(chain_id)) + + def create_transaction_for_staking( + self, + sender: IAccount, + validators: ValidatorsSigners, + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + rewards_address: Optional[Address] = None, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_staking( + sender=sender.address, + validators_file=validators, + amount=native_amount, + rewards_address=rewards_address, + ) + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_topping_up( + self, + sender: IAccount, + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_topping_up( + sender=sender.address, + amount=native_amount, + ) + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_unstaking( + self, + sender: IAccount, + keys: list[ValidatorPublicKey], + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_unstaking( + sender=sender.address, + public_keys=keys, + ) + transaction.value = native_amount + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_unjailing( + self, + sender: IAccount, + keys: list[ValidatorPublicKey], + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_unjailing( + sender=sender.address, + public_keys=keys, + amount=native_amount, + ) + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_unbonding( + self, + sender: IAccount, + keys: list[ValidatorPublicKey], + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_unbonding( + sender=sender.address, + public_keys=keys, + ) + transaction.value = native_amount + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_changing_rewards_address( + self, + sender: IAccount, + rewards_address: Address, + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_changing_rewards_address( + sender=sender.address, + rewards_address=rewards_address, + ) + transaction.value = native_amount + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_claiming( + self, + sender: IAccount, + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_claiming( + sender=sender.address, + ) + transaction.value = native_amount + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_unstaking_nodes( + self, + sender: IAccount, + keys: list[ValidatorPublicKey], + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_unstaking_nodes( + sender=sender.address, + public_keys=keys, + ) + transaction.value = native_amount + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_unstaking_tokens( + self, + sender: IAccount, + value: int, + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_unstaking_tokens( + sender=sender.address, + amount=value, + ) + transaction.value = native_amount + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_unbonding_nodes( + self, + sender: IAccount, + keys: list[ValidatorPublicKey], + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_unbonding_nodes( + sender=sender.address, + public_keys=keys, + ) + transaction.value = native_amount + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_unbonding_tokens( + self, + sender: IAccount, + value: int, + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_unbonding_tokens( + sender=sender.address, + amount=value, + ) + transaction.value = native_amount + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_cleaning_registered_data( + self, + sender: IAccount, + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_cleaning_registered_data( + sender=sender.address, + ) + transaction.value = native_amount + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction + + def create_transaction_for_restaking_unstaked_nodes( + self, + sender: IAccount, + keys: list[ValidatorPublicKey], + native_amount: int, + gas_limit: int, + gas_price: int, + nonce: int, + version: int, + options: int, + guardian_account: Optional[IAccount] = None, + guardian_address: Optional[Address] = None, + relayer_account: Optional[IAccount] = None, + relayer_address: Optional[Address] = None, + guardian_service_url: str = "", + guardian_2fa_code: str = "", + ) -> Transaction: + transaction = self.factory.create_transaction_for_restaking_unstaked_nodes( + sender=sender.address, + public_keys=keys, + ) + transaction.value = native_amount + transaction.gas_price = gas_price + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.guardian = guardian_address + transaction.relayer = relayer_address + + if gas_limit: + transaction.gas_limit = gas_limit + + self.sign_transaction( + transaction=transaction, + sender=sender, + guardian=guardian_account, + relayer=relayer_account, + guardian_service_url=guardian_service_url, + guardian_2fa_code=guardian_2fa_code, + ) + + return transaction diff --git a/multiversx_sdk_cli/validators/__init__.py b/multiversx_sdk_cli/validators/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/multiversx_sdk_cli/validators/core.py b/multiversx_sdk_cli/validators/core.py deleted file mode 100644 index 238288c7..00000000 --- a/multiversx_sdk_cli/validators/core.py +++ /dev/null @@ -1,614 +0,0 @@ -import logging -from pathlib import Path -from typing import Any, Optional, Union - -from multiversx_sdk import Address, Transaction -from multiversx_sdk.abi import ( - AddressValue, - BigUIntValue, - BytesValue, - Serializer, - U32Value, -) - -from multiversx_sdk_cli import utils -from multiversx_sdk_cli.config import MetaChainSystemSCsCost, get_address_hrp -from multiversx_sdk_cli.constants import GAS_PER_DATA_BYTE, MIN_GAS_LIMIT -from multiversx_sdk_cli.interfaces import IAccount -from multiversx_sdk_cli.transactions import TransactionsController -from multiversx_sdk_cli.validators.validators_file import ValidatorsFile - -logger = logging.getLogger("validators") - -VALIDATORS_SMART_CONTRACT_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000001ffff" - - -class ValidatorsController: - def __init__(self, chain_id: str) -> None: - self.transactions_controller = TransactionsController(chain_id) - self.serializer = Serializer() - - def create_transaction_for_staking( - self, - sender: IAccount, - validators_file: Path, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - rewards_address: Optional[Address] = None, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - validators = ValidatorsFile(validators_file) - data = self.prepare_transaction_data_for_stake( - node_operator=sender.address, - validators_file=validators, - rewards_address=rewards_address, - ) - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.STAKE, validators.get_num_of_nodes()) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def prepare_transaction_data_for_stake( - self, - node_operator: Address, - validators_file: ValidatorsFile, - rewards_address: Union[Address, None], - ) -> str: - num_of_nodes = validators_file.get_num_of_nodes() - - call_arguments: list[Any] = [] - call_arguments.append(U32Value(num_of_nodes)) - - validator_signers = validators_file.load_signers() - - for validator in validator_signers: - signed_message = validator.sign(node_operator.get_public_key()) - - call_arguments.append(BytesValue(validator.secret_key.generate_public_key().buffer)) - call_arguments.append(BytesValue(signed_message)) - - if rewards_address: - call_arguments.append(AddressValue.new_from_address(rewards_address)) - - data = "stake@" + self.serializer.serialize(call_arguments) - return data - - def create_transaction_for_topping_up( - self, - sender: IAccount, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - data = "stake" - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.STAKE, 1) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_unstaking( - self, - sender: IAccount, - keys: str, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - parsed_keys, num_keys = utils.parse_keys(keys) - data = f"unStake{parsed_keys}" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNSTAKE, num_keys) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_unjailing( - self, - sender: IAccount, - keys: str, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - parsed_keys, num_keys = utils.parse_keys(keys) - data = f"unJail{parsed_keys}" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNJAIL, num_keys) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_unbonding( - self, - sender: IAccount, - keys: str, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - parsed_keys, num_keys = utils.parse_keys(keys) - data = f"unBond{parsed_keys}" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNBOND, num_keys) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_changing_rewards_address( - self, - sender: IAccount, - rewards_address: Address, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - data = f"changeRewardAddress@{rewards_address.to_hex()}" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.CHANGE_REWARD_ADDRESS) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_claiming( - self, - sender: IAccount, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - data = "claim" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.CLAIM) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_unstaking_nodes( - self, - sender: IAccount, - keys: str, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - parsed_keys, num_keys = utils.parse_keys(keys) - data = f"unStakeNodes{parsed_keys}" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNSTAKE, num_keys) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_unstaking_tokens( - self, - sender: IAccount, - value: int, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - data = f"unStakeTokens@{self.serializer.serialize([BigUIntValue(value)])}" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNSTAKE_TOKENS) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_unbonding_nodes( - self, - sender: IAccount, - keys: str, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - parsed_keys, num_keys = utils.parse_keys(keys) - data = f"unBondNodes{parsed_keys}" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNBOND, num_keys) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_unbonding_tokens( - self, - sender: IAccount, - value: int, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - data = f"unBondTokens@{self.serializer.serialize([BigUIntValue(value)])}" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.UNBOND_TOKENS) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_cleaning_registered_data( - self, - sender: IAccount, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - data = "cleanRegisteredData" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.CLEAN_REGISTERED_DATA) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def create_transaction_for_restaking_unstaked_nodes( - self, - sender: IAccount, - keys: str, - native_amount: int, - gas_limit: int, - gas_price: int, - nonce: int, - version: int, - options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", - ) -> Transaction: - parsed_keys, num_keys = utils.parse_keys(keys) - data = f"reStakeUnStakedNodes{parsed_keys}" - - if not gas_limit: - gas_limit = self.estimate_system_sc_call(data, MetaChainSystemSCsCost.RESTAKE_UNSTAKED_NODES, num_keys) - - receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()) - - return self.transactions_controller.create_transaction( - sender=sender, - receiver=receiver, - native_amount=native_amount, - gas_limit=gas_limit, - gas_price=gas_price, - nonce=nonce, - version=version, - options=options, - data=data, - guardian_account=guardian_account, - guardian_address=guardian_address, - relayer_account=relayer_account, - relayer_address=relayer_address, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, - ) - - def estimate_system_sc_call(self, transaction_data: str, base_cost: int, factor: int = 1): - num_bytes = len(transaction_data) - gas_limit = MIN_GAS_LIMIT + num_bytes * GAS_PER_DATA_BYTE - gas_limit += factor * base_cost - return gas_limit diff --git a/multiversx_sdk_cli/validators/validators_file.py b/multiversx_sdk_cli/validators/validators_file.py deleted file mode 100644 index c0060f44..00000000 --- a/multiversx_sdk_cli/validators/validators_file.py +++ /dev/null @@ -1,56 +0,0 @@ -import json -from pathlib import Path - -from multiversx_sdk import ValidatorPEM, ValidatorPublicKey, ValidatorSigner - -from multiversx_sdk_cli import guards -from multiversx_sdk_cli.errors import CannotReadValidatorsData - - -class ValidatorsFile: - def __init__(self, validators_file_path: Path): - self.validators_file_path = validators_file_path - self._validators_data = self._read_json_file_validators() - - def get_num_of_nodes(self) -> int: - return len(self._validators_data.get("validators", [])) - - def get_validators_list(self): - return self._validators_data.get("validators", []) - - def load_signers(self) -> list[ValidatorSigner]: - signers: list[ValidatorSigner] = [] - for validator in self.get_validators_list(): - pem_file = self._load_validator_pem(validator) - validator_signer = ValidatorSigner(pem_file.secret_key) - signers.append(validator_signer) - - return signers - - def load_public_keys(self) -> list[ValidatorPublicKey]: - public_keys: list[ValidatorPublicKey] = [] - - for validator in self.get_validators_list(): - pem_file = self._load_validator_pem(validator) - public_keys.append(pem_file.secret_key.generate_public_key()) - - return public_keys - - def _load_validator_pem(self, validator: dict[str, str]) -> ValidatorPEM: - # Get path of "pemFile", make it absolute - validator_pem = Path(validator.get("pemFile", "")).expanduser() - validator_pem = ( - validator_pem if validator_pem.is_absolute() else self.validators_file_path.parent / validator_pem - ) - - return ValidatorPEM.from_file(validator_pem) - - def _read_json_file_validators(self): - val_file = self.validators_file_path.expanduser() - guards.is_file(val_file) - with open(self.validators_file_path, "r") as json_file: - try: - data = json.load(json_file) - except Exception: - raise CannotReadValidatorsData() - return data From 422a056dbebcfcb0624bcaab33dcb6ce1d0ced2b Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 6 Mar 2025 15:31:42 +0200 Subject: [PATCH 60/84] fix omit fields --- multiversx_sdk_cli/utils.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/multiversx_sdk_cli/utils.py b/multiversx_sdk_cli/utils.py index 183d4cd7..3b658423 100644 --- a/multiversx_sdk_cli/utils.py +++ b/multiversx_sdk_cli/utils.py @@ -11,8 +11,6 @@ import toml -from multiversx_sdk_cli import errors - logger = logging.getLogger("utils") @@ -43,12 +41,9 @@ def default(self, o: Any) -> Any: return super().default(o) -def omit_fields(data: Any, fields: list[str] = []) -> dict[str, Any]: - if isinstance(data, dict): - for field in fields: - data.pop(field, None) - return data - raise errors.ProgrammingError("omit_fields: only dictionaries are supported.") +def omit_fields(data: dict[str, Any], fields: list[str] = []) -> None: + for field in fields: + data.pop(field, None) def untar(archive_path: Path, destination_folder: Path) -> None: From 406b4997c1258129a0f3889b1c2a992e60255910 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 6 Mar 2025 15:39:51 +0200 Subject: [PATCH 61/84] fix path to validators file --- multiversx_sdk_cli/tests/test_cli_validators_localnet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/tests/test_cli_validators_localnet.py b/multiversx_sdk_cli/tests/test_cli_validators_localnet.py index 7086ce05..4a5197df 100644 --- a/multiversx_sdk_cli/tests/test_cli_validators_localnet.py +++ b/multiversx_sdk_cli/tests/test_cli_validators_localnet.py @@ -15,7 +15,7 @@ @pytest.mark.require_localnet def test_stake(): - validators_json = testdata_path / "validators_ci.json" + validators_json = testdata_path / "validators_ci.pem" # Stake with recall nonce return_code = main( From a7e31042edf54fcc4ae3926f8a19062fb5ad0b67 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 11 Mar 2025 15:20:32 +0200 Subject: [PATCH 62/84] fixes after review --- multiversx_sdk_cli/cli_delegation.py | 30 +++++++++---------- multiversx_sdk_cli/cli_validators.py | 11 ++++--- .../tests/test_cli_staking_provider.py | 8 ++--- .../tests/test_cli_validators.py | 4 +-- .../tests/test_cli_validators_localnet.py | 4 +-- 5 files changed, 28 insertions(+), 29 deletions(-) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index d1907569..5b1fb2a9 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -64,7 +64,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "Add new nodes must be called by the contract owner", ) sub.add_argument( - "--validators-file", required=True, type=str, help="a PEM file describing the Nodes; can contain multiple nodes" + "--validators-pem", required=True, type=str, help="a PEM file holding the BLS keys; can contain multiple nodes" ) sub.add_argument( "--delegation-contract", @@ -82,8 +82,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "remove-nodes", "Remove nodes must be called by the contract owner", ) - sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") - sub.add_argument("--validators-file", type=str, help="a PEM file describing the Nodes; can contain multiple nodes") + sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes as CSV (addrA,addrB)") + sub.add_argument("--validators-pem", type=str, help="a PEM file holding the BLS keys; can contain multiple nodes") sub.add_argument( "--delegation-contract", required=True, @@ -100,8 +100,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "stake-nodes", "Stake nodes must be called by the contract owner", ) - sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") - sub.add_argument("--validators-file", type=str, help="a PEM file describing the Nodes; can contain multiple nodes") + sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes as CSV (addrA,addrB)") + sub.add_argument("--validators-pem", type=str, help="a PEM file holding the BLS keys; can contain multiple nodes") sub.add_argument( "--delegation-contract", required=True, @@ -118,8 +118,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "unbond-nodes", "Unbond nodes must be called by the contract owner", ) - sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") - sub.add_argument("--validators-file", type=str, help="a PEM file describing the Nodes; can contain multiple nodes") + sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes as CSV (addrA,addrB)") + sub.add_argument("--validators-pem", type=str, help="a PEM file holding the BLS keys; can contain multiple nodes") sub.add_argument( "--delegation-contract", required=True, @@ -135,8 +135,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "unstake-nodes", "Unstake nodes must be called by the contract owner", ) - sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") - sub.add_argument("--validators-file", type=str, help="a PEM file describing the Nodes; can contain multiple nodes") + sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes as CSV (addrA,addrB)") + sub.add_argument("--validators-pem", type=str, help="a PEM file holding the BLS keys; can contain multiple nodes") sub.add_argument( "--delegation-contract", required=True, @@ -152,8 +152,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "unjail-nodes", "Unjail nodes must be called by the contract owner", ) - sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes") - sub.add_argument("--validators-file", type=str, help="a PEM file describing the Nodes; can contain multiple nodes") + sub.add_argument("--bls-keys", help="a list with the bls keys of the nodes as CSV (addrA,addrB)") + sub.add_argument("--validators-pem", type=str, help="a PEM file holding the BLS keys; can contain multiple nodes") sub.add_argument( "--delegation-contract", required=True, @@ -505,7 +505,7 @@ def add_new_nodes(args: Any): def _get_public_keys_and_signed_messages(args: Any) -> tuple[list[ValidatorPublicKey], list[bytes]]: - validators_file_path = Path(args.validators_file).expanduser() + validators_file_path = Path(args.validators_pem).expanduser() validators_file = ValidatorsSigners.new_from_pem(validators_file_path) signers = validators_file.get_signers() @@ -559,7 +559,7 @@ def _load_validators_public_keys(args: Any) -> list[ValidatorPublicKey]: if args.bls_keys: return _parse_public_bls_keys(args.bls_keys) - validators_file_path = Path(args.validators_file).expanduser() + validators_file_path = Path(args.validators_pem).expanduser() validators_file = ValidatorsSigners.new_from_pem(validators_file_path) return validators_file.get_public_keys() @@ -609,10 +609,10 @@ def stake_nodes(args: Any): def _check_if_either_bls_keys_or_validators_file_are_provided(args: Any): bls_keys = args.bls_keys - validators_file = args.validators_file + validators_file = args.validators_pem if not bls_keys and not validators_file: - raise errors.BadUsage("No bls keys or validators file provided. Use either `--bls-keys` or `--validators-file`") + raise errors.BadUsage("No bls keys or validators file provided. Use either `--bls-keys` or `--validators-pem`") def unbond_nodes(args: Any): diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index f6bc44f7..31399aa6 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -26,7 +26,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: _add_common_arguments(args, sub) sub.add_argument("--reward-address", default="", help="the reward address") sub.add_argument( - "--validators-file", + "--validators-pem", required=not (utils.is_arg_present(args, "--top-up")), help="a PEM file describing the nodes; can contain multiple nodes", ) @@ -34,7 +34,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "--top-up", action="store_true", default=False, - required=not (utils.is_arg_present(args, "--validators-file")), + required=not (utils.is_arg_present(args, "--validators-pem")), help="Stake value for top up", ) sub.set_defaults(func=do_stake) @@ -182,10 +182,10 @@ def do_stake(args: Any): guardian_2fa_code=args.guardian_2fa_code, ) else: - validators_file = _load_validators_signers(args.validators_file) + validators_signers = _load_validators_signers(args.validators_pem) tx = controller.create_transaction_for_staking( sender=sender, - validators=validators_file, + validators=validators_signers, native_amount=native_amount, gas_limit=gas_limit, gas_price=args.gas_price, @@ -212,8 +212,7 @@ def _get_validators_controller(args: Any): def _load_validators_signers(validators_pem: str) -> ValidatorsSigners: validators_file_path = Path(validators_pem).expanduser() - validators_file = ValidatorsSigners.new_from_pem(validators_file_path) - return validators_file + return ValidatorsSigners.new_from_pem(validators_file_path) def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: diff --git a/multiversx_sdk_cli/tests/test_cli_staking_provider.py b/multiversx_sdk_cli/tests/test_cli_staking_provider.py index a0fc6c47..7b8319e2 100644 --- a/multiversx_sdk_cli/tests/test_cli_staking_provider.py +++ b/multiversx_sdk_cli/tests/test_cli_staking_provider.py @@ -91,7 +91,7 @@ def test_add_nodes(capsys: Any): [ "staking-provider", "add-nodes", - "--validators-file", + "--validators-pem", str(validators_file), "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", @@ -127,7 +127,7 @@ def test_add_nodes_with_gas_limit(capsys: Any): [ "staking-provider", "add-nodes", - "--validators-file", + "--validators-pem", str(validators_file), "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", @@ -193,7 +193,7 @@ def test_remove_nodes_with_validators_file(capsys: Any): [ "staking-provider", "remove-nodes", - "--validators-file", + "--validators-pem", str(validators_file), "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", @@ -223,7 +223,7 @@ def test_stake_nodes_with_bls_keys(capsys: Any): [ "staking-provider", "stake-nodes", - "--validators-file", + "--validators-pem", str(validators_file), "--delegation-contract", "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh", diff --git a/multiversx_sdk_cli/tests/test_cli_validators.py b/multiversx_sdk_cli/tests/test_cli_validators.py index 9187cb3d..f90ce3ca 100644 --- a/multiversx_sdk_cli/tests/test_cli_validators.py +++ b/multiversx_sdk_cli/tests/test_cli_validators.py @@ -26,7 +26,7 @@ def test_stake(capsys: Any): str(alice_pem), "--value", "2500000000000000000000", - "--validators-file", + "--validators-pem", str(validators_pem), "--reward-address", reward_address, @@ -108,7 +108,7 @@ def test_stake_with_relayer_and_guardian(capsys: Any): str(alice_pem), "--value", "2500000000000000000000", - "--validators-file", + "--validators-pem", str(validators_pem), "--reward-address", reward_address, diff --git a/multiversx_sdk_cli/tests/test_cli_validators_localnet.py b/multiversx_sdk_cli/tests/test_cli_validators_localnet.py index 4a5197df..89149f18 100644 --- a/multiversx_sdk_cli/tests/test_cli_validators_localnet.py +++ b/multiversx_sdk_cli/tests/test_cli_validators_localnet.py @@ -26,7 +26,7 @@ def test_stake(): str(alice_pem), "--value", "2500000000000000000000", - "--validators-file", + "--validators-pem", str(validators_json), "--reward-address", reward_address, @@ -48,7 +48,7 @@ def test_stake(): str(alice_pem), "--value", "2500000000000000000000", - "--validators-file", + "--validators-pem", str(validators_json), "--reward-address", reward_address, From 2a1d8910d5448a1826d0bf38369817073da8e9a5 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 13 Mar 2025 11:36:48 +0200 Subject: [PATCH 63/84] integrate transaction awaiter --- multiversx_sdk_cli/transactions.py | 32 ++++++++++-------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 0fe2e2c8..cc261624 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -1,10 +1,10 @@ import json import logging -import time from typing import Any, Optional, Protocol, TextIO, Union from multiversx_sdk import ( Address, + AwaitingOptions, SmartContractResult, TokenTransfer, Transaction, @@ -23,6 +23,9 @@ logger = logging.getLogger("transactions") +ONE_SECOND_IN_MILLISECONDS = 1000 + + # fmt: off class INetworkProvider(Protocol): def send_transaction(self, transaction: Transaction) -> bytes: @@ -30,6 +33,9 @@ def send_transaction(self, transaction: Transaction) -> bytes: def get_transaction(self, transaction_hash: Union[bytes, str]) -> TransactionOnNetwork: ... + + def await_transaction_completed(self, transaction_hash: Union[bytes, str], options: Optional[AwaitingOptions] = None) -> TransactionOnNetwork: + ... # fmt: on @@ -100,28 +106,12 @@ def send_and_wait_for_result(transaction: Transaction, proxy: INetworkProvider, if not transaction.signature: raise errors.TransactionIsNotSigned() - txOnNetwork = _send_transaction_and_wait_for_result(proxy, transaction, timeout) - return txOnNetwork - + options = AwaitingOptions(timeout_in_milliseconds=timeout * ONE_SECOND_IN_MILLISECONDS) -def _send_transaction_and_wait_for_result( - proxy: INetworkProvider, payload: Transaction, num_seconds_timeout: int = 100 -) -> TransactionOnNetwork: - AWAIT_TRANSACTION_PERIOD = 5 - - tx_hash = proxy.send_transaction(payload) - num_periods_to_wait = int(num_seconds_timeout / AWAIT_TRANSACTION_PERIOD) - - for _ in range(0, num_periods_to_wait): - time.sleep(AWAIT_TRANSACTION_PERIOD) - - tx = proxy.get_transaction(tx_hash) - if tx.status.is_completed: - return tx - else: - logger.info("Transaction not yet done.") + tx_hash = proxy.send_transaction(transaction) + tx_on_network = proxy.await_transaction_completed(tx_hash, options) - raise errors.KnownError("Took too long to get transaction.") + return tx_on_network def load_transaction_from_file(f: TextIO) -> Transaction: From 85598164a01a3d67bb6b30679bdb9c96a8c3a9df Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 13 Mar 2025 11:43:56 +0200 Subject: [PATCH 64/84] remove unused package for mypy --- mypy.ini | 3 --- 1 file changed, 3 deletions(-) diff --git a/mypy.ini b/mypy.ini index 5673978b..dbeebb11 100644 --- a/mypy.ini +++ b/mypy.ini @@ -14,6 +14,3 @@ ignore_missing_imports = True [mypy-semver.*] ignore_missing_imports = True - -[mypy-requests_cache.*] -ignore_missing_imports = True From 3f3e08189b778ce267420403a1d3bc9dcca83c7e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 14 Mar 2025 14:06:53 +0200 Subject: [PATCH 65/84] wip: improve guardian flow --- multiversx_sdk_cli/cli_shared.py | 79 +++++++++++++++------ multiversx_sdk_cli/cli_transactions.py | 1 - multiversx_sdk_cli/guardian_relayer_data.py | 16 +++++ multiversx_sdk_cli/interfaces.py | 24 +------ requirements.txt | 1 - 5 files changed, 74 insertions(+), 47 deletions(-) create mode 100644 multiversx_sdk_cli/guardian_relayer_data.py diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 890c36b2..29a2b1e9 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -22,6 +22,7 @@ ) from multiversx_sdk_cli.constants import DEFAULT_GAS_PRICE, DEFAULT_TX_VERSION from multiversx_sdk_cli.errors import ArgumentsNotProvidedError, IncorrectWalletError +from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.simulation import Simulator from multiversx_sdk_cli.transactions import send_and_wait_for_result @@ -118,28 +119,10 @@ def add_tx_args( default=DEFAULT_TX_VERSION, help="the transaction version (default: %(default)s)", ) + sub.add_argument("--options", type=int, default=0, help="the transaction options (default: %(default)s)") - sub.add_argument("--relayer", help="the bech32 address of the relayer") - - add_guardian_args(sub) - - sub.add_argument("--options", type=int, default=0, help="the transaction options (default: 0)") - - -def add_guardian_args(sub: Any): - sub.add_argument("--guardian", type=str, help="the address of the guradian", default="") - sub.add_argument( - "--guardian-service-url", - type=str, - help="the url of the guardian service", - default="", - ) - sub.add_argument( - "--guardian-2fa-code", - type=str, - help="the 2fa code for the guardian", - default="", - ) + sub.add_argument("--relayer", type=str, help="the bech32 address of the relayer", default="") + sub.add_argument("--guardian", type=str, help="the bech32 address of the guradian", default="") def add_wallet_args(args: list[str], sub: Any): @@ -174,6 +157,18 @@ def add_wallet_args(args: list[str], sub: Any): def add_guardian_wallet_args(args: list[str], sub: Any): + sub.add_argument( + "--guardian-service-url", + type=str, + help="the url of the guardian service", + default="", + ) + sub.add_argument( + "--guardian-2fa-code", + type=str, + help="the 2fa code for the guardian", + default="", + ) sub.add_argument( "--guardian-pem", help="🔑 the PEM file, if keyfile not provided", @@ -313,6 +308,33 @@ def get_guardian_address(guardian: Union[IAccount, None], args: Any) -> Union[Ad return address_from_account or address_from_args +# check if account is guarded, api/proxy agnostic +# check if guardian is active +# get guardian address +# get guardian service (if TCS have the url ready) + +# def fetch_guardian_address_from_the_network(address: str, proxy_url: str) -> Address: +# network_provider_config = config.get_config_for_network_providers() +# proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) +# return proxy.get_account(Address.new_from_bech32(address)).address + + +def fetch_guardian_info(address: str, proxy_url: str) -> Union[tuple[Address, str], None]: + network_provider_config = config.get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) + + response = proxy.do_get_generic(f"/address/{address}/guardian-data").to_dictionary() + guardian_data = response.get("guardianData", {}) + + if not bool(guardian_data.get("guarded", "")): + return None + + guardian_address = Address.new_from_bech32(guardian_data.get("address", "")) + service_url = guardian_data.get("serviceUID", "") + + return None + + def get_relayer_address(relayer: Union[IAccount, None], args: Any) -> Union[Address, None]: address_from_account = relayer.address if relayer else None address_from_args = Address.new_from_bech32(args.relayer) if hasattr(args, "relayer") and args.relayer else None @@ -445,7 +467,7 @@ def prepare_sender(args: Any): return sender -def prepare_guardian(args: Any): +def prepare_guardian(args: Any) -> tuple[Union[IAccount, None], Union[Address, None]]: """Reurns a tuple containing the guardians's account and the account's address. If no account or address were provided, will return (None, None).""" guardian = load_guardian_account(args) @@ -453,9 +475,20 @@ def prepare_guardian(args: Any): return guardian, guardian_address -def prepare_relayer(args: Any): +def prepare_relayer(args: Any) -> tuple[Union[IAccount, None], Union[Address, None]]: """Reurns a tuple containing the relayer's account and the account's address. If no account or address were provided, will return (None, None).""" relayer = load_relayer_account(args) relayer_address = get_relayer_address(relayer, args) return relayer, relayer_address + + +def prepare_guardian_relayer_data(args: Any) -> GuardianRelayerData: + guardian, guardian_address = prepare_guardian(args) + relayer, relayer_address = prepare_relayer(args) + return GuardianRelayerData( + guardian=guardian, + guardian_address=guardian_address, + relayer=relayer, + relayer_address=relayer_address, + ) diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 9dfe4819..3333d6db 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -86,7 +86,6 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_outfile_arg(sub, what="the signed transaction") cli_shared.add_broadcast_args(sub) cli_shared.add_proxy_arg(sub) - cli_shared.add_guardian_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) sub.set_defaults(func=sign_transaction) diff --git a/multiversx_sdk_cli/guardian_relayer_data.py b/multiversx_sdk_cli/guardian_relayer_data.py new file mode 100644 index 00000000..a47268f6 --- /dev/null +++ b/multiversx_sdk_cli/guardian_relayer_data.py @@ -0,0 +1,16 @@ +from dataclasses import dataclass +from typing import Optional + +from multiversx_sdk import Address + +from multiversx_sdk_cli.interfaces import IAccount + + +@dataclass +class GuardianRelayerData: + guardian: Optional[IAccount] = None + guardian_address: Optional[Address] = None + guardian_service_url: Optional[str] = None + guardian_2fa_code: Optional[str] = None + relayer: Optional[IAccount] = None + relayer_address: Optional[Address] = None diff --git a/multiversx_sdk_cli/interfaces.py b/multiversx_sdk_cli/interfaces.py index b6d70e18..8a533e7c 100644 --- a/multiversx_sdk_cli/interfaces.py +++ b/multiversx_sdk_cli/interfaces.py @@ -1,29 +1,9 @@ -from typing import Any, Dict, Protocol +from typing import Any, Protocol from multiversx_sdk import Address, Transaction # fmt: off -class ITransaction(Protocol): - sender: str - receiver: str - gas_limit: int - chain_id: str - nonce: int - value: int - sender_username: str - receiver_username: str - gas_price: int - data: bytes - version: int - options: int - guardian: str - signature: bytes - guardian_signature: bytes - relayer: str - relayer_signature: bytes - - class IAccount(Protocol): address: Address @@ -32,5 +12,5 @@ def sign_transaction(self, transaction: Transaction) -> bytes: class ISimulateResponse(Protocol): - def to_dictionary(self) -> Dict[str, Any]: + def to_dictionary(self) -> dict[str, Any]: ... diff --git a/requirements.txt b/requirements.txt index 540ea6d4..04388f50 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,6 @@ types-toml requests>=2.32.0,<3.0.0 types-requests prettytable -types-prettytable ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 From 685bbe5786887d43015309188d5009fc0db3ceb6 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 14 Mar 2025 14:25:41 +0200 Subject: [PATCH 66/84] add specific prefix for address arguments --- multiversx_sdk_cli/contracts.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 43c9e7b5..4ef53491 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -36,6 +36,8 @@ FALSE_STR_LOWER = "false" TRUE_STR_LOWER = "true" STR_PREFIX = "str:" +ADDRESS_PREFIX = "addr:" +MAINCHAIN_ADDRESS_HRP = "erd" # fmt: off @@ -262,6 +264,10 @@ def _prepare_args_for_factory(self, arguments: list[str]) -> list[Any]: args.append(BytesValue(self._hex_to_bytes(arg))) elif arg.isnumeric(): args.append(BigUIntValue(int(arg))) + elif arg.startswith(ADDRESS_PREFIX): + args.append(AddressValue.new_from_address(Address.new_from_bech32(arg[len(ADDRESS_PREFIX) :]))) + elif arg.startswith(MAINCHAIN_ADDRESS_HRP): + args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) elif arg.startswith(get_address_hrp()): args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) elif arg.lower() == FALSE_STR_LOWER: From 93859e1c3ebd4c43bee833ad534b15133e3bd998 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 14 Mar 2025 15:12:22 +0200 Subject: [PATCH 67/84] install sdk-py from GH brach --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 94b24246..bf5d9eed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "ledgercomm[hid]", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk[ledger]==1.1.0" + "multiversx-sdk @ git+https://github.com/multiversx/mx-sdk-py@feat/next" ] [project.scripts] From 90315eae16f3f62c4c285a53d3f9af353edc8fa0 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 14 Mar 2025 15:15:29 +0200 Subject: [PATCH 68/84] update pyproject.toml --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index bf5d9eed..65e16565 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,9 @@ classifiers = [ "Intended Audience :: Developers" ] +[tool.hatch.metadata] +allow-direct-references = true + dependencies = [ "toml>=0.10.2", "requests>=2.32.0,<3.0.0", From 64348b30f7ec989054e4987c7b7ec47589ce2415 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 14 Mar 2025 16:04:01 +0200 Subject: [PATCH 69/84] update to sdk py beta realease --- pyproject.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 65e16565..fa3ec00b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,9 +19,6 @@ classifiers = [ "Intended Audience :: Developers" ] -[tool.hatch.metadata] -allow-direct-references = true - dependencies = [ "toml>=0.10.2", "requests>=2.32.0,<3.0.0", @@ -29,7 +26,7 @@ dependencies = [ "ledgercomm[hid]", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk @ git+https://github.com/multiversx/mx-sdk-py@feat/next" + "multiversx-sdk[ledger]==1.2.0b0" ] [project.scripts] From da14733db2e13183a593436771a24fe6af40913e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 17 Mar 2025 16:43:47 +0200 Subject: [PATCH 70/84] automatic fetching for guardian from the network --- .../base_transactions_controller.py | 20 +- multiversx_sdk_cli/cli_delegation.py | 234 +++++------ multiversx_sdk_cli/cli_shared.py | 118 +++++- multiversx_sdk_cli/cli_transactions.py | 17 +- multiversx_sdk_cli/cli_validators.py | 163 +++----- multiversx_sdk_cli/constants.py | 4 + .../delegation/staking_provider.py | 379 ++++++++---------- multiversx_sdk_cli/dns.py | 17 +- .../tests/test_cli_validators.py | 6 +- multiversx_sdk_cli/tests/test_transactions.py | 35 +- multiversx_sdk_cli/transactions.py | 28 +- multiversx_sdk_cli/validators.py | 274 ++++++------- 12 files changed, 600 insertions(+), 695 deletions(-) diff --git a/multiversx_sdk_cli/base_transactions_controller.py b/multiversx_sdk_cli/base_transactions_controller.py index 596e26b4..858d81ca 100644 --- a/multiversx_sdk_cli/base_transactions_controller.py +++ b/multiversx_sdk_cli/base_transactions_controller.py @@ -2,6 +2,10 @@ from multiversx_sdk import LedgerAccount, Transaction, TransactionComputer +from multiversx_sdk_cli.constants import ( + EXTRA_GAS_LIMIT_FOR_GUARDED_TRANSACTIONS, + EXTRA_GAS_LIMIT_FOR_RELAYED_TRANSACTIONS, +) from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.interfaces import IAccount @@ -16,8 +20,8 @@ def sign_transaction( sender: Optional[IAccount] = None, guardian: Optional[IAccount] = None, relayer: Optional[IAccount] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_service_url: Optional[str] = None, + guardian_2fa_code: Optional[str] = None, ): """Signs the transaction using the sender's account and, if required, additionally signs with the guardian's and relayer's accounts. Ensures the appropriate transaction options are set as needed.""" self._set_options_for_guarded_transaction_if_needed(transaction) @@ -34,6 +38,14 @@ def sign_transaction( ) self._sign_relayed_transaction_if_relayer(transaction, relayer) + def add_extra_gas_limit_if_required(self, transaction: Transaction): + """In case of guarded or relayed transactions, extra gas limit is added.""" + if transaction.guardian: + transaction.gas_limit += EXTRA_GAS_LIMIT_FOR_GUARDED_TRANSACTIONS + + if transaction.relayer: + transaction.gas_limit += EXTRA_GAS_LIMIT_FOR_RELAYED_TRANSACTIONS + def _set_options_for_guarded_transaction_if_needed(self, transaction: Transaction): if transaction.guardian: transaction_computer = TransactionComputer() @@ -58,8 +70,8 @@ def _sign_guarded_transaction_if_guardian( self, transaction: Transaction, guardian: Union[IAccount, None], - guardian_service_url: str, - guardian_2fa_code: str, + guardian_service_url: Union[str, None], + guardian_2fa_code: Union[str, None], ) -> Transaction: # If the guardian account is provided, we sign locally. Otherwise, we reach for the trusted cosign service. if guardian: diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 5b1fb2a9..87bb8e07 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -427,8 +427,10 @@ def do_create_delegation_contract(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 delegation = _get_delegation_controller(args) @@ -443,12 +445,7 @@ def do_create_delegation_contract(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -475,8 +472,10 @@ def add_new_nodes(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 public_keys, signed_messages = _get_public_keys_and_signed_messages(args) @@ -493,12 +492,7 @@ def add_new_nodes(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -527,8 +521,10 @@ def remove_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 public_keys = _load_validators_public_keys(args) @@ -544,12 +540,7 @@ def remove_nodes(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -579,8 +570,10 @@ def stake_nodes(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 public_keys = _load_validators_public_keys(args) @@ -596,12 +589,7 @@ def stake_nodes(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -620,8 +608,10 @@ def unbond_nodes(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 public_keys = _load_validators_public_keys(args) @@ -637,12 +627,7 @@ def unbond_nodes(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -653,8 +638,10 @@ def unstake_nodes(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 public_keys = _load_validators_public_keys(args) @@ -670,12 +657,7 @@ def unstake_nodes(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -686,8 +668,10 @@ def unjail_nodes(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 public_keys = _load_validators_public_keys(args) @@ -703,12 +687,7 @@ def unjail_nodes(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -718,8 +697,10 @@ def delegate(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 value = int(args.value) @@ -734,12 +715,7 @@ def delegate(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -749,8 +725,10 @@ def claim_rewards(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -764,12 +742,7 @@ def claim_rewards(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -778,8 +751,10 @@ def redelegate_rewards(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -793,12 +768,7 @@ def redelegate_rewards(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -807,8 +777,10 @@ def undelegate(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 value = int(args.value) @@ -823,12 +795,7 @@ def undelegate(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -838,8 +805,10 @@ def withdraw(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -853,12 +822,7 @@ def withdraw(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -868,8 +832,10 @@ def change_service_fee(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -884,12 +850,7 @@ def change_service_fee(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -899,8 +860,10 @@ def modify_delegation_cap(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -915,12 +878,7 @@ def modify_delegation_cap(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -930,8 +888,10 @@ def automatic_activation(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -947,12 +907,7 @@ def automatic_activation(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -962,8 +917,10 @@ def redelegate_cap(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -979,12 +936,7 @@ def redelegate_cap(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -994,8 +946,10 @@ def set_metadata(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -1012,12 +966,7 @@ def set_metadata(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -1027,8 +976,10 @@ def make_new_contract_from_validator_data(args: Any): validate_arguments(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -1043,12 +994,7 @@ def make_new_contract_from_validator_data(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 29a2b1e9..95242830 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -20,8 +20,16 @@ load_password, load_relayer_password, ) -from multiversx_sdk_cli.constants import DEFAULT_GAS_PRICE, DEFAULT_TX_VERSION -from multiversx_sdk_cli.errors import ArgumentsNotProvidedError, IncorrectWalletError +from multiversx_sdk_cli.constants import ( + DEFAULT_GAS_PRICE, + DEFAULT_TX_VERSION, + TCS_SERVICE_ID, +) +from multiversx_sdk_cli.errors import ( + ArgumentsNotProvidedError, + BadUsage, + IncorrectWalletError, +) from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.simulation import Simulator @@ -29,6 +37,19 @@ from multiversx_sdk_cli.utils import log_explorer_transaction from multiversx_sdk_cli.ux import show_warning +trusted_cosigner_service_url_by_chain_id = { + "1": "https://tools.multiversx.com/guardian", + "D": "https://devnet-tools.multiversx.com/guardian", + "T": "https://testnet-tools.multiversx.com/guardian", +} + + +def get_trusted_cosigner_service_url_by_chain_id(chain_id: str) -> str: + try: + return trusted_cosigner_service_url_by_chain_id[chain_id] + except: + raise BadUsage(f"Could not get Trusted Cosigner Service Url. No match found for chain id: {chain_id}") + def wider_help_formatter(prog: Text): return argparse.RawDescriptionHelpFormatter(prog, max_help_position=50, width=120) @@ -308,31 +329,84 @@ def get_guardian_address(guardian: Union[IAccount, None], args: Any) -> Union[Ad return address_from_account or address_from_args -# check if account is guarded, api/proxy agnostic -# check if guardian is active -# get guardian address -# get guardian service (if TCS have the url ready) +def get_guardian_and_relayer_data(sender: str, args: Any) -> GuardianRelayerData: + guardian = load_guardian_account(args) -# def fetch_guardian_address_from_the_network(address: str, proxy_url: str) -> Address: -# network_provider_config = config.get_config_for_network_providers() -# proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) -# return proxy.get_account(Address.new_from_bech32(address)).address + # get guardian address from account or from cli args + guardian_address = get_guardian_address(guardian, args) + relayer = load_relayer_account(args) + relayer_address = get_relayer_address(relayer, args) -def fetch_guardian_info(address: str, proxy_url: str) -> Union[tuple[Address, str], None]: - network_provider_config = config.get_config_for_network_providers() - proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) + guardian_and_relayer_data = GuardianRelayerData( + guardian=guardian, + guardian_address=guardian_address, + relayer=relayer, + relayer_address=relayer_address, + ) - response = proxy.do_get_generic(f"/address/{address}/guardian-data").to_dictionary() - guardian_data = response.get("guardianData", {}) + _get_guardian_data_from_network(sender, args, guardian_and_relayer_data) + return guardian_and_relayer_data + + +def _get_guardian_data_from_network(sender: str, args: Any, guardian_and_relayer_data: GuardianRelayerData): + """Updates the `guardian_and_relayer_data` parameter, that is later used.""" + + # if guardian not provided, get guardian from the network + if not guardian_and_relayer_data.guardian_address: + guardian_data = _get_guardian_data(sender, args.proxy) + + if guardian_data: + guardian_and_relayer_data.guardian_address = Address.new_from_bech32(guardian_data["guardian_address"]) + + # if tcs is used, set url, else get service url from args + tcs_url = guardian_data["cosigner_service_url"] + guardian_and_relayer_data.guardian_service_url = tcs_url if tcs_url else args.guardian_service_url + + if guardian_and_relayer_data.guardian_service_url: + guardian_and_relayer_data.guardian_2fa_code = _get_2fa_code(args) + + +def _get_guardian_data(address: str, proxy_url: str) -> Union[dict[str, str], None]: + if not proxy_url: + return None + + guardian_data = _fetch_guardian_data(address, proxy_url) if not bool(guardian_data.get("guarded", "")): return None - guardian_address = Address.new_from_bech32(guardian_data.get("address", "")) - service_url = guardian_data.get("serviceUID", "") + active_guardian = guardian_data.get("activeGuardian", {}) - return None + guardian_address = active_guardian.get("address", "") + service_id = active_guardian.get("serviceUID", "") + + cosigner_service_url = "" + + if service_id == TCS_SERVICE_ID: + chain_id = _fetch_chain_id(proxy_url) + cosigner_service_url = get_trusted_cosigner_service_url_by_chain_id(chain_id) + + return { + "guardian_address": guardian_address, + "cosigner_service_url": cosigner_service_url, + } + + +def _fetch_guardian_data(address: str, proxy_url: str) -> dict[str, Any]: + network_provider_config = config.get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) + + response = proxy.do_get_generic(f"/address/{address}/guardian-data").to_dictionary() + guardian_data: dict[str, Any] = response.get("guardianData", {}) + return guardian_data + + +def _get_2fa_code(args: Any) -> str: + code: str = args.guardian_2fa_code + if not code: + code = input("Please enter the two factor authentication code: ") + return code def get_relayer_address(relayer: Union[IAccount, None], args: Any) -> Union[Address, None]: @@ -381,9 +455,7 @@ def get_current_nonce_for_address(address: Address, proxy_url: Union[str, None]) def get_chain_id(chain_id: str, proxy_url: str) -> str: if chain_id and proxy_url: - network_provider_config = config.get_config_for_network_providers() - proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) - fetched_chain_id = proxy.get_network_config().chain_id + fetched_chain_id = _fetch_chain_id(proxy_url) if chain_id != fetched_chain_id: show_warning( @@ -394,6 +466,10 @@ def get_chain_id(chain_id: str, proxy_url: str) -> str: if chain_id: return chain_id + return _fetch_chain_id(proxy_url) + + +def _fetch_chain_id(proxy_url: str) -> str: network_provider_config = config.get_config_for_network_providers() proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) return proxy.get_network_config().chain_id diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 3333d6db..27785f43 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -121,16 +121,14 @@ def create_transaction(args: Any): validate_chain_id_args(args) sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) if args.data_file: args.data = Path(args.data_file).read_text() - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) - native_amount = int(args.value) gas_limit = int(args.gas_limit) if args.gas_limit else 0 @@ -151,12 +149,7 @@ def create_transaction(args: Any): options=args.options, token_transfers=transfers, data=args.data, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 31399aa6..7d06feb9 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -156,8 +156,10 @@ def validate_args(args: Any) -> None: def do_stake(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -174,12 +176,7 @@ def do_stake(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) else: validators_signers = _load_validators_signers(args.validators_pem) @@ -193,12 +190,7 @@ def do_stake(args: Any): version=args.version, options=args.options, rewards_address=rewards_address, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -228,8 +220,10 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def do_unstake(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -245,12 +239,7 @@ def do_unstake(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -259,8 +248,10 @@ def do_unstake(args: Any): def do_unjail(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -276,12 +267,7 @@ def do_unjail(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -290,8 +276,10 @@ def do_unjail(args: Any): def do_unbond(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -307,12 +295,7 @@ def do_unbond(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -321,8 +304,10 @@ def do_unbond(args: Any): def change_reward_address(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -338,12 +323,7 @@ def change_reward_address(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -352,8 +332,10 @@ def change_reward_address(args: Any): def do_claim(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -367,12 +349,7 @@ def do_claim(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -381,8 +358,10 @@ def do_claim(args: Any): def do_unstake_nodes(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -399,12 +378,7 @@ def do_unstake_nodes(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -413,8 +387,10 @@ def do_unstake_nodes(args: Any): def do_unstake_tokens(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) value = int(args.unstake_value) @@ -430,12 +406,7 @@ def do_unstake_tokens(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -444,8 +415,10 @@ def do_unstake_tokens(args: Any): def do_unbond_nodes(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -462,12 +435,7 @@ def do_unbond_nodes(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -476,8 +444,10 @@ def do_unbond_nodes(args: Any): def do_unbond_tokens(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) value = int(args.unbond_value) @@ -493,12 +463,7 @@ def do_unbond_tokens(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -507,8 +472,10 @@ def do_unbond_tokens(args: Any): def do_clean_registered_data(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -522,12 +489,7 @@ def do_clean_registered_data(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) @@ -536,8 +498,10 @@ def do_clean_registered_data(args: Any): def do_restake_unstaked_nodes(args: Any): validate_args(args) sender = cli_shared.prepare_sender(args) - guardian, guardian_address = cli_shared.prepare_guardian(args) - relayer, relayer_address = cli_shared.prepare_relayer(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) gas_limit = args.gas_limit if args.gas_limit else 0 @@ -553,12 +517,7 @@ def do_restake_unstaked_nodes(args: Any): nonce=sender.nonce, version=args.version, options=args.options, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/constants.py b/multiversx_sdk_cli/constants.py index 556611ac..9a997542 100644 --- a/multiversx_sdk_cli/constants.py +++ b/multiversx_sdk_cli/constants.py @@ -15,3 +15,7 @@ DEFAULT_GAS_PRICE = 1000000000 MIN_GAS_LIMIT = 50000 + +TCS_SERVICE_ID = "MultiversXTCSService" +EXTRA_GAS_LIMIT_FOR_GUARDED_TRANSACTIONS = 50_000 +EXTRA_GAS_LIMIT_FOR_RELAYED_TRANSACTIONS = 50_000 diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation/staking_provider.py index 748405a7..17a8c377 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -1,5 +1,3 @@ -from typing import Optional - from multiversx_sdk import ( Address, DelegationTransactionsFactory, @@ -12,6 +10,7 @@ from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.errors import BadUsage +from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount DELEGATION_MANAGER_SC_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000004ffff" @@ -32,12 +31,7 @@ def prepare_transaction_for_new_delegation_contract( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_new_delegation_contract( sender=owner.address, @@ -49,8 +43,10 @@ def prepare_transaction_for_new_delegation_contract( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -58,10 +54,10 @@ def prepare_transaction_for_new_delegation_contract( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -78,12 +74,7 @@ def prepare_transaction_for_adding_nodes( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_adding_nodes( sender=owner.address, @@ -96,8 +87,10 @@ def prepare_transaction_for_adding_nodes( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -105,10 +98,10 @@ def prepare_transaction_for_adding_nodes( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -124,12 +117,7 @@ def prepare_transaction_for_removing_nodes( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_removing_nodes( sender=owner.address, @@ -141,8 +129,10 @@ def prepare_transaction_for_removing_nodes( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -150,10 +140,10 @@ def prepare_transaction_for_removing_nodes( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -169,12 +159,7 @@ def prepare_transaction_for_staking_nodes( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_staking_nodes( sender=owner.address, @@ -186,8 +171,10 @@ def prepare_transaction_for_staking_nodes( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -195,10 +182,10 @@ def prepare_transaction_for_staking_nodes( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -214,12 +201,7 @@ def prepare_transaction_for_unbonding_nodes( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_unbonding_nodes( sender=owner.address, @@ -231,8 +213,10 @@ def prepare_transaction_for_unbonding_nodes( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -240,10 +224,10 @@ def prepare_transaction_for_unbonding_nodes( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -259,12 +243,7 @@ def prepare_transaction_for_unstaking_nodes( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_unstaking_nodes( sender=owner.address, @@ -276,8 +255,10 @@ def prepare_transaction_for_unstaking_nodes( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -285,10 +266,10 @@ def prepare_transaction_for_unstaking_nodes( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -304,12 +285,7 @@ def prepare_transaction_for_unjailing_nodes( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_unjailing_nodes( sender=owner.address, @@ -321,8 +297,10 @@ def prepare_transaction_for_unjailing_nodes( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -330,10 +308,10 @@ def prepare_transaction_for_unjailing_nodes( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -348,12 +326,7 @@ def prepare_transaction_for_delegating( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_delegating( sender=owner.address, @@ -364,8 +337,10 @@ def prepare_transaction_for_delegating( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -373,10 +348,10 @@ def prepare_transaction_for_delegating( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -391,12 +366,7 @@ def prepare_transaction_for_claiming_rewards( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_claiming_rewards( sender=owner.address, delegation_contract=delegation_contract @@ -406,8 +376,10 @@ def prepare_transaction_for_claiming_rewards( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -415,10 +387,10 @@ def prepare_transaction_for_claiming_rewards( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -433,12 +405,7 @@ def prepare_transaction_for_redelegating_rewards( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_redelegating_rewards( sender=owner.address, delegation_contract=delegation_contract @@ -448,8 +415,10 @@ def prepare_transaction_for_redelegating_rewards( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -457,10 +426,10 @@ def prepare_transaction_for_redelegating_rewards( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -475,12 +444,7 @@ def prepare_transaction_for_undelegating( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_undelegating( sender=owner.address, @@ -491,8 +455,10 @@ def prepare_transaction_for_undelegating( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -500,10 +466,10 @@ def prepare_transaction_for_undelegating( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -518,12 +484,7 @@ def prepare_transaction_for_withdrawing( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_withdrawing( sender=owner.address, delegation_contract=delegation_contract @@ -533,8 +494,10 @@ def prepare_transaction_for_withdrawing( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -542,10 +505,10 @@ def prepare_transaction_for_withdrawing( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -561,12 +524,7 @@ def prepare_transaction_for_changing_service_fee( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_changing_service_fee( sender=owner.address, @@ -578,8 +536,10 @@ def prepare_transaction_for_changing_service_fee( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -587,10 +547,10 @@ def prepare_transaction_for_changing_service_fee( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -606,12 +566,7 @@ def prepare_transaction_for_modifying_delegation_cap( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_modifying_delegation_cap( sender=owner.address, @@ -623,8 +578,10 @@ def prepare_transaction_for_modifying_delegation_cap( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -632,10 +589,10 @@ def prepare_transaction_for_modifying_delegation_cap( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -652,12 +609,7 @@ def prepare_transaction_for_automatic_activation( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: if set and unset: raise BadUsage("Cannot set and unset at the same time") @@ -678,8 +630,10 @@ def prepare_transaction_for_automatic_activation( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -687,10 +641,10 @@ def prepare_transaction_for_automatic_activation( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -707,12 +661,7 @@ def prepare_transaction_for_redelegate_cap( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: if set and unset: raise BadUsage("Cannot set and unset at the same time") @@ -733,8 +682,10 @@ def prepare_transaction_for_redelegate_cap( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -742,10 +693,10 @@ def prepare_transaction_for_redelegate_cap( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -763,12 +714,7 @@ def prepare_transaction_for_setting_metadata( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self._factory.create_transaction_for_setting_metadata( sender=owner.address, @@ -782,8 +728,10 @@ def prepare_transaction_for_setting_metadata( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) if gas_limit: tx.gas_limit = gas_limit @@ -791,10 +739,10 @@ def prepare_transaction_for_setting_metadata( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -811,12 +759,7 @@ def prepare_transaction_for_creating_delegation_contract_from_validator( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: receiver = Address.new_from_hex(DELEGATION_MANAGER_SC_ADDRESS_HEX, get_address_hrp()) @@ -834,8 +777,8 @@ def prepare_transaction_for_creating_delegation_contract_from_validator( nonce=nonce, version=version, options=options, - guardian=guardian_address, - relayer=relayer_address, + guardian=guardian_and_relayer_data.guardian_address, + relayer=guardian_and_relayer_data.relayer_address, gas_price=gas_price, value=value, ) @@ -846,10 +789,10 @@ def prepare_transaction_for_creating_delegation_contract_from_validator( self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 8060840d..95ab0a85 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -67,12 +67,10 @@ def register(args: Any): validate_chain_id_args(args) sender = cli_shared.prepare_sender(args) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) native_amount = int(args.value) @@ -92,12 +90,7 @@ def register(args: Any): version=args.version, options=args.options, data=data, - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) cli_shared.send_or_simulate(tx, args) diff --git a/multiversx_sdk_cli/tests/test_cli_validators.py b/multiversx_sdk_cli/tests/test_cli_validators.py index f90ce3ca..c482673d 100644 --- a/multiversx_sdk_cli/tests/test_cli_validators.py +++ b/multiversx_sdk_cli/tests/test_cli_validators.py @@ -134,7 +134,7 @@ def test_stake_with_relayer_and_guardian(capsys: Any): assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" assert tx["value"] == "2500000000000000000000" assert tx["nonce"] == 0 - assert tx["gasLimit"] == 11029500 + assert tx["gasLimit"] == 11129500 assert tx["chainID"] == "localnet" assert tx["version"] == 2 assert tx["options"] == 2 @@ -142,11 +142,11 @@ def test_stake_with_relayer_and_guardian(capsys: Any): assert tx["relayer"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" assert ( tx["signature"] - == "9ecca226c3e5913906a5f22971a0d84bb7a8c652e309ffab4073dea8bbd88caccc2ad9e33b6929f16acdedb676ad574ad66d520abd5b398469fa0cff3fc7ca03" + == "87e80ebb4bb31837a268ec209353615e77ecf8799353dff9a7c6c78f049d7a3ebca715840db14428e98925fc3b8b9137dbbe35ea33affe2420fa3b2f1e701d06" ) assert ( tx["guardianSignature"] - == "12c1ee05d34282555d85f7786dc0e7ffbce960de88fb75ba81a237bd1f2cc175f50ee42e60b2857bf2cd49d02de12a4017f1c95f14910fcc27bc7cb16b41ce04" + == "afb5e45bec458dae4b5ab80f8c6048a15faaeb2bcfaad90e6707de62bf3a931b440835ae8ec1dd767d52a5ed290583fe73b5302801ecfcd8e7c5908fdbf9b001" ) assert ( data diff --git a/multiversx_sdk_cli/tests/test_transactions.py b/multiversx_sdk_cli/tests/test_transactions.py index b5420b53..8a8c4e4c 100644 --- a/multiversx_sdk_cli/tests/test_transactions.py +++ b/multiversx_sdk_cli/tests/test_transactions.py @@ -2,6 +2,7 @@ from multiversx_sdk import Account, Address +from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.transactions import TransactionsController testdata = Path(__file__).parent / "testdata" @@ -12,6 +13,7 @@ class TestTransactionsController: alice = Account.new_from_pem(testdata / "alice.pem") def test_create_transaction_without_data_and_value(self): + guardian_relayer_data = GuardianRelayerData() transaction = self.controller.create_transaction( sender=self.alice, receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), @@ -21,6 +23,7 @@ def test_create_transaction_without_data_and_value(self): nonce=7, version=2, options=0, + guardian_and_relayer_data=guardian_relayer_data, ) assert transaction.sender == self.alice.address @@ -43,6 +46,7 @@ def test_create_transaction_without_data_and_value(self): ) def test_create_transfer_transaction(self): + guardian_relayer_data = GuardianRelayerData() transaction = self.controller.create_transaction( sender=self.alice, receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), @@ -52,6 +56,7 @@ def test_create_transfer_transaction(self): nonce=7, version=2, options=0, + guardian_and_relayer_data=guardian_relayer_data, ) assert transaction.sender == self.alice.address @@ -74,6 +79,7 @@ def test_create_transfer_transaction(self): ) def test_create_transaction_with_data(self): + guardian_relayer_data = GuardianRelayerData() transaction = self.controller.create_transaction( sender=self.alice, receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), @@ -84,6 +90,7 @@ def test_create_transaction_with_data(self): version=2, options=0, data="testdata", + guardian_and_relayer_data=guardian_relayer_data, ) assert transaction.sender == self.alice.address @@ -106,6 +113,11 @@ def test_create_transaction_with_data(self): ) def test_create_guarded_transaction(self): + guardian_relayer_data = GuardianRelayerData( + guardian=Account.new_from_pem(testdata / "testUser2.pem"), + guardian_address=Address.new_from_bech32("erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4"), + ) + transaction = self.controller.create_transaction( sender=self.alice, receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), @@ -116,8 +128,7 @@ def test_create_guarded_transaction(self): version=2, options=0, data="testdata", - guardian_address=Address.new_from_bech32("erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4"), - guardian_account=Account.new_from_pem(testdata / "testUser2.pem"), + guardian_and_relayer_data=guardian_relayer_data, ) assert transaction.sender == self.alice.address @@ -146,6 +157,11 @@ def test_create_guarded_transaction(self): ) def test_create_relayed_transaction(self): + guardian_relayer_data = GuardianRelayerData( + relayer=Account.new_from_pem(testdata / "testUser2.pem"), + relayer_address=Address.new_from_bech32("erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4"), + ) + transaction = self.controller.create_transaction( sender=self.alice, receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), @@ -156,8 +172,7 @@ def test_create_relayed_transaction(self): version=2, options=0, data="testdata", - relayer_address=Address.new_from_bech32("erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4"), - relayer_account=Account.new_from_pem(testdata / "testUser2.pem"), + guardian_and_relayer_data=guardian_relayer_data, ) assert transaction.sender == self.alice.address @@ -186,6 +201,13 @@ def test_create_relayed_transaction(self): ) def test_create_guarded_relayed_transaction(self): + guardian_relayer_data = GuardianRelayerData( + guardian=Account.new_from_pem(testdata / "testUser.pem"), + guardian_address=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), + relayer=Account.new_from_pem(testdata / "testUser2.pem"), + relayer_address=Address.new_from_bech32("erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4"), + ) + transaction = self.controller.create_transaction( sender=self.alice, receiver=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), @@ -196,10 +218,7 @@ def test_create_guarded_relayed_transaction(self): version=2, options=0, data="testdata", - guardian_address=Address.new_from_bech32("erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5"), - guardian_account=Account.new_from_pem(testdata / "testUser.pem"), - relayer_address=Address.new_from_bech32("erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4"), - relayer_account=Account.new_from_pem(testdata / "testUser2.pem"), + guardian_and_relayer_data=guardian_relayer_data, ) assert transaction.sender == self.alice.address diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index cc261624..0d8d9772 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -18,6 +18,7 @@ from multiversx_sdk_cli import errors from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.constants import MIN_GAS_LIMIT +from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount logger = logging.getLogger("transactions") @@ -54,14 +55,9 @@ def create_transaction( nonce: int, version: int, options: int, + guardian_and_relayer_data: GuardianRelayerData, token_transfers: Optional[list[TokenTransfer]] = None, data: Optional[str] = None, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", ) -> Transaction: # if no value, token transfers or data provided, create plain transaction if not native_amount and not token_transfers and not data: @@ -80,23 +76,25 @@ def create_transaction( data=data.encode() if data else None, ) - if gas_limit: - transaction.gas_limit = gas_limit - transaction.gas_price = gas_price transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) + + if gas_limit: + transaction.gas_limit = gas_limit self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction diff --git a/multiversx_sdk_cli/validators.py b/multiversx_sdk_cli/validators.py index d5729621..74796834 100644 --- a/multiversx_sdk_cli/validators.py +++ b/multiversx_sdk_cli/validators.py @@ -10,6 +10,7 @@ ) from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController +from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.transactions import TransactionsController @@ -29,13 +30,8 @@ def create_transaction_for_staking( nonce: int, version: int, options: int, + guardian_and_relayer_data: GuardianRelayerData, rewards_address: Optional[Address] = None, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", ) -> Transaction: transaction = self.factory.create_transaction_for_staking( sender=sender.address, @@ -47,8 +43,10 @@ def create_transaction_for_staking( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -56,10 +54,10 @@ def create_transaction_for_staking( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -73,12 +71,7 @@ def create_transaction_for_topping_up( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_topping_up( sender=sender.address, @@ -88,8 +81,10 @@ def create_transaction_for_topping_up( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -97,10 +92,10 @@ def create_transaction_for_topping_up( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -115,12 +110,7 @@ def create_transaction_for_unstaking( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_unstaking( sender=sender.address, @@ -131,8 +121,10 @@ def create_transaction_for_unstaking( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -140,10 +132,10 @@ def create_transaction_for_unstaking( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -158,12 +150,7 @@ def create_transaction_for_unjailing( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_unjailing( sender=sender.address, @@ -174,8 +161,10 @@ def create_transaction_for_unjailing( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -183,10 +172,10 @@ def create_transaction_for_unjailing( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -201,12 +190,7 @@ def create_transaction_for_unbonding( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_unbonding( sender=sender.address, @@ -217,8 +201,10 @@ def create_transaction_for_unbonding( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -226,10 +212,10 @@ def create_transaction_for_unbonding( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -244,12 +230,7 @@ def create_transaction_for_changing_rewards_address( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_changing_rewards_address( sender=sender.address, @@ -260,8 +241,10 @@ def create_transaction_for_changing_rewards_address( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -269,10 +252,10 @@ def create_transaction_for_changing_rewards_address( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -286,12 +269,7 @@ def create_transaction_for_claiming( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_claiming( sender=sender.address, @@ -301,8 +279,10 @@ def create_transaction_for_claiming( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -310,10 +290,10 @@ def create_transaction_for_claiming( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -328,12 +308,7 @@ def create_transaction_for_unstaking_nodes( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_unstaking_nodes( sender=sender.address, @@ -344,8 +319,10 @@ def create_transaction_for_unstaking_nodes( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -353,10 +330,10 @@ def create_transaction_for_unstaking_nodes( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -371,12 +348,7 @@ def create_transaction_for_unstaking_tokens( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_unstaking_tokens( sender=sender.address, @@ -387,8 +359,10 @@ def create_transaction_for_unstaking_tokens( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -396,10 +370,10 @@ def create_transaction_for_unstaking_tokens( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -414,12 +388,7 @@ def create_transaction_for_unbonding_nodes( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_unbonding_nodes( sender=sender.address, @@ -430,8 +399,10 @@ def create_transaction_for_unbonding_nodes( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -439,10 +410,10 @@ def create_transaction_for_unbonding_nodes( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -457,12 +428,7 @@ def create_transaction_for_unbonding_tokens( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_unbonding_tokens( sender=sender.address, @@ -473,8 +439,10 @@ def create_transaction_for_unbonding_tokens( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -482,10 +450,10 @@ def create_transaction_for_unbonding_tokens( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -499,12 +467,7 @@ def create_transaction_for_cleaning_registered_data( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_cleaning_registered_data( sender=sender.address, @@ -514,8 +477,10 @@ def create_transaction_for_cleaning_registered_data( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -523,10 +488,10 @@ def create_transaction_for_cleaning_registered_data( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction @@ -541,12 +506,7 @@ def create_transaction_for_restaking_unstaked_nodes( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: transaction = self.factory.create_transaction_for_restaking_unstaked_nodes( sender=sender.address, @@ -557,8 +517,10 @@ def create_transaction_for_restaking_unstaked_nodes( transaction.nonce = nonce transaction.version = version transaction.options = options - transaction.guardian = guardian_address - transaction.relayer = relayer_address + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(transaction) if gas_limit: transaction.gas_limit = gas_limit @@ -566,10 +528,10 @@ def create_transaction_for_restaking_unstaked_nodes( self.sign_transaction( transaction=transaction, sender=sender, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return transaction From 55ae23c5936d3230f183bc322d201c1f2d8615a5 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 17 Mar 2025 17:09:00 +0200 Subject: [PATCH 71/84] set guardian from args if provided --- multiversx_sdk_cli/cli_shared.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 95242830..21094b01 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -345,7 +345,12 @@ def get_guardian_and_relayer_data(sender: str, args: Any) -> GuardianRelayerData relayer_address=relayer_address, ) - _get_guardian_data_from_network(sender, args, guardian_and_relayer_data) + if guardian_and_relayer_data.guardian_address: + guardian_and_relayer_data.guardian_service_url = args.guardian_service_url + guardian_and_relayer_data.guardian_2fa_code = args.guardian_2fa_code + else: + _get_guardian_data_from_network(sender, args, guardian_and_relayer_data) + return guardian_and_relayer_data @@ -353,18 +358,17 @@ def _get_guardian_data_from_network(sender: str, args: Any, guardian_and_relayer """Updates the `guardian_and_relayer_data` parameter, that is later used.""" # if guardian not provided, get guardian from the network - if not guardian_and_relayer_data.guardian_address: - guardian_data = _get_guardian_data(sender, args.proxy) + guardian_data = _get_guardian_data(sender, args.proxy) - if guardian_data: - guardian_and_relayer_data.guardian_address = Address.new_from_bech32(guardian_data["guardian_address"]) + if guardian_data: + guardian_and_relayer_data.guardian_address = Address.new_from_bech32(guardian_data["guardian_address"]) - # if tcs is used, set url, else get service url from args - tcs_url = guardian_data["cosigner_service_url"] - guardian_and_relayer_data.guardian_service_url = tcs_url if tcs_url else args.guardian_service_url + # if tcs is used, set url, else get service url from args + tcs_url = guardian_data["cosigner_service_url"] + guardian_and_relayer_data.guardian_service_url = tcs_url if tcs_url else args.guardian_service_url - if guardian_and_relayer_data.guardian_service_url: - guardian_and_relayer_data.guardian_2fa_code = _get_2fa_code(args) + if guardian_and_relayer_data.guardian_service_url: + guardian_and_relayer_data.guardian_2fa_code = _get_2fa_code(args) def _get_guardian_data(address: str, proxy_url: str) -> Union[dict[str, str], None]: From 69a4c9c46671c118e7a62d574bc59738e839ae4b Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 17 Mar 2025 17:29:49 +0200 Subject: [PATCH 72/84] fixes after review --- multiversx_sdk_cli/contracts.py | 4 ++++ pyproject.toml | 2 +- requirements.txt | 3 +-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 4ef53491..97916a19 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -267,6 +267,10 @@ def _prepare_args_for_factory(self, arguments: list[str]) -> list[Any]: elif arg.startswith(ADDRESS_PREFIX): args.append(AddressValue.new_from_address(Address.new_from_bech32(arg[len(ADDRESS_PREFIX) :]))) elif arg.startswith(MAINCHAIN_ADDRESS_HRP): + # this flow will be removed in the future + logger.warning( + "Address argument has no prefix. This flow will be removed in the future. Please provide each address using the `addr:` prefix. (e.g. --arguments addr:erd1...)" + ) args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) elif arg.startswith(get_address_hrp()): args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) diff --git a/pyproject.toml b/pyproject.toml index fa3ec00b..88069145 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "ledgercomm[hid]", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk[ledger]==1.2.0b0" + "multiversx-sdk[ledger]==1.2.0" ] [project.scripts] diff --git a/requirements.txt b/requirements.txt index 540ea6d4..1cea0650 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,5 +8,4 @@ ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 -# multiversx-sdk[ledger]==1.1.0 -git+https://github.com/multiversx/mx-sdk-py@feat/next#egg=multiversx_sdk +multiversx-sdk[ledger]==1.2.0 From e6bc09f9aa7bb60bbd10ac96f79d1024e2e35c48 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 18 Mar 2025 13:29:39 +0200 Subject: [PATCH 73/84] remove delegation sub-package and move code --- .../{delegation/staking_provider.py => delegation.py} | 0 multiversx_sdk_cli/delegation/__init__.py | 3 --- 2 files changed, 3 deletions(-) rename multiversx_sdk_cli/{delegation/staking_provider.py => delegation.py} (100%) delete mode 100644 multiversx_sdk_cli/delegation/__init__.py diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation.py similarity index 100% rename from multiversx_sdk_cli/delegation/staking_provider.py rename to multiversx_sdk_cli/delegation.py diff --git a/multiversx_sdk_cli/delegation/__init__.py b/multiversx_sdk_cli/delegation/__init__.py deleted file mode 100644 index ecdc1533..00000000 --- a/multiversx_sdk_cli/delegation/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from multiversx_sdk_cli.delegation.staking_provider import DelegationOperations - -__all__ = ["DelegationOperations"] From ef7410ef94f22c7b504d6f86ddb936f5308597e8 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 18 Mar 2025 14:01:10 +0200 Subject: [PATCH 74/84] fix typo --- multiversx_sdk_cli/cli_shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 21094b01..f3017c8a 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -143,7 +143,7 @@ def add_tx_args( sub.add_argument("--options", type=int, default=0, help="the transaction options (default: %(default)s)") sub.add_argument("--relayer", type=str, help="the bech32 address of the relayer", default="") - sub.add_argument("--guardian", type=str, help="the bech32 address of the guradian", default="") + sub.add_argument("--guardian", type=str, help="the bech32 address of the guardian", default="") def add_wallet_args(args: list[str], sub: Any): From 96964e2352faf31eb1b8f3dd1c82aede66b69a19 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 18 Mar 2025 14:03:30 +0200 Subject: [PATCH 75/84] small readme fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 03722264..e3d60aaf 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Allow `pre-commit` to automatically run on `git commit`: pre-commit install ``` -Above, `requirements.txt` should mirror the **dependencies** section of `setup.py`. +Above, `requirements.txt` should mirror the **dependencies** section of `pyproject.toml`. If using VSCode, restart it or follow these steps: - `Ctrl + Shift + P` From 9a0766adc16244e200f90a5db65c490c3adf69fb Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 19 Mar 2025 11:14:07 +0200 Subject: [PATCH 76/84] use builtin types for type hinting --- multiversx_sdk_cli/cli.py | 10 ++++---- multiversx_sdk_cli/cli_data.py | 8 +++--- multiversx_sdk_cli/cli_deps.py | 4 +-- multiversx_sdk_cli/cli_dns.py | 8 +++--- multiversx_sdk_cli/cli_localnet.py | 4 +-- multiversx_sdk_cli/config.py | 28 ++++++++++----------- multiversx_sdk_cli/contract_verification.py | 6 ++--- multiversx_sdk_cli/docker.py | 5 ++-- multiversx_sdk_cli/errors.py | 4 +-- multiversx_sdk_cli/myprocess.py | 4 +-- multiversx_sdk_cli/simulation.py | 6 ++--- 11 files changed, 43 insertions(+), 44 deletions(-) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 3804bad7..8bf57fd8 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -3,7 +3,7 @@ import logging import sys from argparse import ArgumentParser -from typing import Any, List +from typing import Any import argcomplete from multiversx_sdk import LibraryConfig @@ -28,7 +28,7 @@ logger = logging.getLogger("cli") -def main(cli_args: List[str] = sys.argv[1:]): +def main(cli_args: list[str] = sys.argv[1:]): try: _do_main(cli_args) except errors.KnownError as err: @@ -41,7 +41,7 @@ def main(cli_args: List[str] = sys.argv[1:]): return 0 -def _do_main(cli_args: List[str]): +def _do_main(cli_args: list[str]): utils.ensure_folder(config.SDK_PATH) argv_with_config_args = config.add_config_args(cli_args) parser = setup_parser(argv_with_config_args) @@ -72,7 +72,7 @@ def _do_main(cli_args: List[str]): args.func(args) -def setup_parser(args: List[str]): +def setup_parser(args: list[str]): parser = ArgumentParser( prog="mxpy", usage="mxpy [-h] [-v] [--verbose] COMMAND-GROUP [-h] COMMAND ...", @@ -104,7 +104,7 @@ def setup_parser(args: List[str]): parser.add_argument("--verbose", action="store_true", default=False) subparsers = parser.add_subparsers() - commands: List[Any] = [] + commands: list[Any] = [] commands.append(multiversx_sdk_cli.cli_contracts.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_transactions.setup_parser(args, subparsers)) diff --git a/multiversx_sdk_cli/cli_data.py b/multiversx_sdk_cli/cli_data.py index 6b13f6fa..69ed6ca1 100644 --- a/multiversx_sdk_cli/cli_data.py +++ b/multiversx_sdk_cli/cli_data.py @@ -1,7 +1,7 @@ import logging import os from pathlib import Path -from typing import Any, Dict +from typing import Any from multiversx_sdk_cli import cli_shared, errors, utils, workstation @@ -101,16 +101,16 @@ def load(args: Any): print(value) -def _read_file(use_global: bool) -> Dict[str, Any]: +def _read_file(use_global: bool) -> dict[str, Any]: filename = _get_filename(use_global) if not os.path.isfile(filename): return dict() - data: Dict[str, Any] = utils.read_json_file(filename) + data: dict[str, Any] = utils.read_json_file(filename) return data -def _write_file(use_global: bool, data: Dict[str, Any]): +def _write_file(use_global: bool, data: dict[str, Any]): filename = _get_filename(use_global) utils.write_json_file(str(filename), data) diff --git a/multiversx_sdk_cli/cli_deps.py b/multiversx_sdk_cli/cli_deps.py index b73fd02e..f2638dc9 100644 --- a/multiversx_sdk_cli/cli_deps.py +++ b/multiversx_sdk_cli/cli_deps.py @@ -1,5 +1,5 @@ import logging -from typing import Any, List, Tuple +from typing import Any from multiversx_sdk_cli import cli_shared, config, dependencies, errors from multiversx_sdk_cli.dependencies.install import get_deps_dict @@ -45,7 +45,7 @@ def check(args: Any): if name == "all": all_dependencies = dependencies.get_all_deps() - missing_dependencies: List[Tuple[str, str]] = [] + missing_dependencies: list[tuple[str, str]] = [] for dependency in all_dependencies: tag_to_check: str = config.get_dependency_tag(dependency.key) diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index 1e890b50..987b2d55 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -1,4 +1,4 @@ -from typing import Any, List +from typing import Any from multiversx_sdk import ProxyNetworkProvider from prettytable import PrettyTable @@ -19,7 +19,7 @@ from multiversx_sdk_cli.errors import ArgumentsNotProvidedError -def setup_parser(args: List[str], subparsers: Any) -> Any: +def setup_parser(args: list[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser(subparsers, "dns", "Operations related to the Domain Name Service") subparsers = parser.add_subparsers() @@ -190,7 +190,7 @@ def get_version(args: Any): for shard_id in range(0, 256): address = compute_dns_address_for_shard_id(shard_id) v = version(shard_id, proxy) - t.add_row([shard_id, address.to_bech32(), address.to_hex(), v]) + t.add_row([shard_id, address.to_bech32(), address.to_hex(), v]) # type: ignore print(t) else: shard_id = int(args.shard_id) @@ -201,5 +201,5 @@ def print_dns_addresses_table(args: Any): t = PrettyTable(["Shard ID", "Contract address (bech32)", "Contract address (hex)"]) for shard_id in range(0, 256): address = compute_dns_address_for_shard_id(shard_id) - t.add_row([shard_id, address.to_bech32(), address.to_hex()]) + t.add_row([shard_id, address.to_bech32(), address.to_hex()]) # type: ignore print(t) diff --git a/multiversx_sdk_cli/cli_localnet.py b/multiversx_sdk_cli/cli_localnet.py index 72160751..2f57a1f0 100644 --- a/multiversx_sdk_cli/cli_localnet.py +++ b/multiversx_sdk_cli/cli_localnet.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import Any, List +from typing import Any from multiversx_sdk_cli import cli_shared, ux from multiversx_sdk_cli.constants import ONE_YEAR_IN_SECONDS @@ -17,7 +17,7 @@ logger = logging.getLogger("cli.localnet") -def setup_parser(args: List[str], subparsers: Any) -> Any: +def setup_parser(args: list[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser(subparsers, "localnet", "Set up, start and control localnets") subparsers = parser.add_subparsers() diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index afcf82b2..222e4c7b 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -1,6 +1,6 @@ import os from pathlib import Path -from typing import Any, Dict, List +from typing import Any from multiversx_sdk import NetworkProviderConfig @@ -81,12 +81,12 @@ def delete_value(name: str): write_file(data) -def get_active() -> Dict[str, Any]: +def get_active() -> dict[str, Any]: data = read_file() configs = data.get("configurations", {}) active_config_name: str = data.get("active", "default") - empty_config: Dict[str, Any] = dict() - result: Dict[str, Any] = configs.get(active_config_name, empty_config) + empty_config: dict[str, Any] = dict() + result: dict[str, Any] = configs.get(active_config_name, empty_config) return result @@ -143,7 +143,7 @@ def _guard_valid_config_deletion(name: str): raise errors.ConfigurationProtectedError(name) -def get_defaults() -> Dict[str, Any]: +def get_defaults() -> dict[str, Any]: return { "dependencies.vmtools.tag": "v1.5.24", "dependencies.vmtools.urlTemplate.linux": "https://github.com/multiversx/mx-chain-vm-go/archive/{TAG}.tar.gz", @@ -179,20 +179,20 @@ def resolve_config_path() -> Path: return GLOBAL_CONFIG_PATH -def read_file() -> Dict[str, Any]: +def read_file() -> dict[str, Any]: config_path = resolve_config_path() if config_path.exists(): - data: Dict[str, Any] = utils.read_json_file(config_path) + data: dict[str, Any] = utils.read_json_file(config_path) return data return dict() -def write_file(data: Dict[str, Any]): +def write_file(data: dict[str, Any]): config_path = resolve_config_path() utils.write_json_file(str(config_path), data) -def add_config_args(argv: List[str]) -> List[str]: +def add_config_args(argv: list[str]) -> list[str]: try: command, subcommand, *_ = argv except ValueError: @@ -210,8 +210,8 @@ def add_config_args(argv: List[str]) -> List[str]: return final_args -def determine_final_args(argv: List[str], config_args: Dict[str, Any]) -> List[str]: - extra_args: List[str] = [] +def determine_final_args(argv: list[str], config_args: dict[str, Any]) -> list[str]: + extra_args: list[str] = [] for key, value in config_args.items(): key_arg = f"--{key}" # arguments from the command line override the config @@ -222,9 +222,9 @@ def determine_final_args(argv: List[str], config_args: Dict[str, Any]) -> List[s extra_args.append(key_arg) if value is True: continue - if isinstance(value, List): - for item in value: - extra_args.append(str(item)) + if isinstance(value, list): + for item in value: # type: ignore + extra_args.append(str(item)) # type: ignore else: extra_args.append(str(value)) diff --git a/multiversx_sdk_cli/contract_verification.py b/multiversx_sdk_cli/contract_verification.py index 3a9221c3..28fea012 100644 --- a/multiversx_sdk_cli/contract_verification.py +++ b/multiversx_sdk_cli/contract_verification.py @@ -3,7 +3,7 @@ import logging import time from pathlib import Path -from typing import Any, Optional, Protocol, Tuple +from typing import Any, Optional, Protocol import requests from multiversx_sdk import Address, Message @@ -143,7 +143,7 @@ def query_status_with_task_id(url: str, task_id: str, interval: int = 10): time.sleep(interval) -def _do_post(url: str, payload: Any) -> Tuple[int, str, dict[str, Any]]: +def _do_post(url: str, payload: Any) -> tuple[int, str, dict[str, Any]]: logger.debug(f"_do_post() to {url}") response = requests.post(url, json=payload) @@ -156,7 +156,7 @@ def _do_post(url: str, payload: Any) -> Tuple[int, str, dict[str, Any]]: raise KnownError(f"Cannot parse response from {url}", error) -def _do_get(url: str) -> Tuple[int, str, dict[str, Any]]: +def _do_get(url: str) -> tuple[int, str, dict[str, Any]]: logger.debug(f"_do_get() from {url}") response = requests.get(url) diff --git a/multiversx_sdk_cli/docker.py b/multiversx_sdk_cli/docker.py index 2c087a77..79767326 100644 --- a/multiversx_sdk_cli/docker.py +++ b/multiversx_sdk_cli/docker.py @@ -2,7 +2,6 @@ import os import subprocess from pathlib import Path -from typing import List from multiversx_sdk_cli.errors import KnownError @@ -32,7 +31,7 @@ def run_docker( docker_tty: bool, no_default_platform: bool, ): - docker_mount_args: List[str] = ["--volume", f"{output_path}:/output"] + docker_mount_args: list[str] = ["--volume", f"{output_path}:/output"] if project_path: docker_mount_args.extend(["--volume", f"{project_path}:/project"]) @@ -51,7 +50,7 @@ def run_docker( docker_args += ["--user", f"{str(os.getuid())}:{str(os.getgid())}"] docker_args += ["--rm", image] - entrypoint_args: List[str] = [] + entrypoint_args: list[str] = [] if project_path: entrypoint_args.extend(["--project", "project"]) diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index e00e42e1..be13e41b 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -1,4 +1,4 @@ -from typing import Any, List, Tuple, Union +from typing import Any, Union class KnownError(Exception): @@ -38,7 +38,7 @@ def __init__(self, name: str, tag: str): class DependenciesMissing(KnownError): - def __init__(self, dependencies: List[Tuple[str, str]]): + def __init__(self, dependencies: list[tuple[str, str]]): message = "Dependencies missing: \n" for dependency in dependencies: diff --git a/multiversx_sdk_cli/myprocess.py b/multiversx_sdk_cli/myprocess.py index a8a72277..810db319 100644 --- a/multiversx_sdk_cli/myprocess.py +++ b/multiversx_sdk_cli/myprocess.py @@ -1,7 +1,7 @@ import logging import subprocess from pathlib import Path -from typing import Any, List, Optional, Union +from typing import Any, Optional, Union from multiversx_sdk_cli import errors @@ -9,7 +9,7 @@ def run_process( - args: List[str], + args: list[str], env: Any = None, dump_to_stdout: bool = True, cwd: Optional[Union[str, Path]] = None, diff --git a/multiversx_sdk_cli/simulation.py b/multiversx_sdk_cli/simulation.py index d2984b2c..8ba3a35a 100644 --- a/multiversx_sdk_cli/simulation.py +++ b/multiversx_sdk_cli/simulation.py @@ -1,5 +1,5 @@ from collections import OrderedDict -from typing import Any, Dict, Protocol +from typing import Any, Protocol from multiversx_sdk import Transaction, TransactionOnNetwork @@ -17,8 +17,8 @@ class Simulation(ISerializable): def __init__(self, simulate_response: TransactionOnNetwork) -> None: self.simulation_response = simulate_response - def to_dictionary(self) -> Dict[str, Any]: - dictionary: Dict[str, Any] = OrderedDict() + def to_dictionary(self) -> dict[str, Any]: + dictionary: dict[str, Any] = OrderedDict() dictionary["execution"] = self.simulation_response.raw return dictionary From 4a6eac6e59418686c1548aa52620008459378107 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 21 Mar 2025 16:59:30 +0200 Subject: [PATCH 77/84] fixes after review --- multiversx_sdk_cli/cli_shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index f3017c8a..00b1743b 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -368,7 +368,7 @@ def _get_guardian_data_from_network(sender: str, args: Any, guardian_and_relayer guardian_and_relayer_data.guardian_service_url = tcs_url if tcs_url else args.guardian_service_url if guardian_and_relayer_data.guardian_service_url: - guardian_and_relayer_data.guardian_2fa_code = _get_2fa_code(args) + guardian_and_relayer_data.guardian_2fa_code = _ask_for_2fa_code(args) def _get_guardian_data(address: str, proxy_url: str) -> Union[dict[str, str], None]: @@ -406,7 +406,7 @@ def _fetch_guardian_data(address: str, proxy_url: str) -> dict[str, Any]: return guardian_data -def _get_2fa_code(args: Any) -> str: +def _ask_for_2fa_code(args: Any) -> str: code: str = args.guardian_2fa_code if not code: code = input("Please enter the two factor authentication code: ") From 1de19439cf3d79769b97d3b922f655cca9982312 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Sat, 22 Mar 2025 13:43:27 +0200 Subject: [PATCH 78/84] fix SimpleNamespace serialization --- multiversx_sdk_cli/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/multiversx_sdk_cli/utils.py b/multiversx_sdk_cli/utils.py index 3b658423..f15518a4 100644 --- a/multiversx_sdk_cli/utils.py +++ b/multiversx_sdk_cli/utils.py @@ -7,6 +7,7 @@ import tarfile import zipfile from pathlib import Path +from types import SimpleNamespace from typing import Any, Optional, Protocol, Union, runtime_checkable import toml @@ -38,6 +39,9 @@ def default(self, o: Any) -> Any: return o.to_dictionary() if isinstance(o, bytes): return o.hex() + # needed because sdk-py returns SimpleNamespace objects that the json library does not know to serialize + if isinstance(o, SimpleNamespace): + return o.__dict__ return super().default(o) From 7249f25c4cc1aebfcf5c89eb7409f25b1aa9f766 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 24 Mar 2025 15:03:54 +0200 Subject: [PATCH 79/84] remove prettytable dependency --- multiversx_sdk_cli/cli_dns.py | 36 +++++++++++++++++++++-------------- mypy.ini | 3 --- pyproject.toml | 1 - requirements.txt | 2 -- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index 1e890b50..45da048a 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -1,7 +1,8 @@ from typing import Any, List from multiversx_sdk import ProxyNetworkProvider -from prettytable import PrettyTable +from rich.console import Console +from rich.table import Table from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.config import get_config_for_network_providers @@ -179,27 +180,34 @@ def get_version(args: Any): config = get_config_for_network_providers() proxy = ProxyNetworkProvider(url=args.proxy, config=config) if args.all: - t = PrettyTable( - [ - "Shard ID", - "Contract address (bech32)", - "Contract address (hex)", - "Version", - ] - ) + table = Table(title="DNS Version") + table.add_column("Shard ID") + table.add_column("Contract address (bech32)") + table.add_column("Contract address (hex)") + table.add_column("Version") + for shard_id in range(0, 256): address = compute_dns_address_for_shard_id(shard_id) v = version(shard_id, proxy) - t.add_row([shard_id, address.to_bech32(), address.to_hex(), v]) - print(t) + table.add_row(str(shard_id), address.to_bech32(), address.to_hex(), v) + + console = Console() + console.print(table) else: shard_id = int(args.shard_id) print(version(shard_id, proxy)) def print_dns_addresses_table(args: Any): - t = PrettyTable(["Shard ID", "Contract address (bech32)", "Contract address (hex)"]) + table = Table(title="DNS Addresses") + + table.add_column("Shard ID") + table.add_column("Contract address (bech32)") + table.add_column("Contract address (hex)") + for shard_id in range(0, 256): address = compute_dns_address_for_shard_id(shard_id) - t.add_row([shard_id, address.to_bech32(), address.to_hex()]) - print(t) + table.add_row(str(shard_id), address.to_bech32(), address.to_hex()) + + console = Console() + console.print(table) diff --git a/mypy.ini b/mypy.ini index dbeebb11..510006a4 100644 --- a/mypy.ini +++ b/mypy.ini @@ -9,8 +9,5 @@ ignore_missing_imports = True [mypy-ledgercomm.*] ignore_missing_imports = True -[mypy-prettytable.*] -ignore_missing_imports = True - [mypy-semver.*] ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml index 88069145..dfd8f334 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,6 @@ classifiers = [ dependencies = [ "toml>=0.10.2", "requests>=2.32.0,<3.0.0", - "prettytable", "ledgercomm[hid]", "rich==13.3.4", "argcomplete==3.2.2", diff --git a/requirements.txt b/requirements.txt index 1cea0650..7f295a29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,8 +2,6 @@ toml>=0.10.2 types-toml requests>=2.32.0,<3.0.0 types-requests -prettytable -types-prettytable ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 From 3325150de23b76572be02513774d4f44f03fd240 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 24 Mar 2025 15:06:20 +0200 Subject: [PATCH 80/84] remove empty line --- multiversx_sdk_cli/cli_dns.py | 1 - 1 file changed, 1 deletion(-) diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index 45da048a..180cd07d 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -200,7 +200,6 @@ def get_version(args: Any): def print_dns_addresses_table(args: Any): table = Table(title="DNS Addresses") - table.add_column("Shard ID") table.add_column("Contract address (bech32)") table.add_column("Contract address (hex)") From a3acbcf172661f0d5bd1539b067e3c69c3e44562 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 24 Mar 2025 18:03:01 +0200 Subject: [PATCH 81/84] bump version & generate CLI.md file --- CLI.md | 3023 ++++++++++++++++----------- CLI.md.sh | 31 +- multiversx_sdk_cli/cli_contracts.py | 2 +- pyproject.toml | 2 +- 4 files changed, 1823 insertions(+), 1235 deletions(-) diff --git a/CLI.md b/CLI.md index cc3b856e..c388fdd5 100644 --- a/CLI.md +++ b/CLI.md @@ -20,10 +20,10 @@ mxpy targets a broad audience of users and developers. See: - https://docs.multiversx.com/sdk-and-tools/sdk-py - https://docs.multiversx.com/sdk-and-tools/sdk-py/mxpy-cli - + COMMAND GROUPS: - {contract,tx,validator,account,ledger,wallet,deps,config,localnet,data,staking-provider,dns,faucet} + {contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet} TOP-LEVEL OPTIONS: -h, --help show this help message and exit @@ -33,12 +33,12 @@ TOP-LEVEL OPTIONS: ---------------------- COMMAND GROUPS summary ---------------------- -contract Build, deploy, upgrade and interact with Smart Contracts +contract Deploy, upgrade and interact with Smart Contracts tx Create and broadcast Transactions validator Stake, UnStake, UnBond, Unjail and other actions useful for Validators -account Get Account data (nonce, balance) from the Network ledger Get Ledger App addresses and version wallet Create wallet, derive secret key from mnemonic, bech32 address helpers etc. +validator-wallet Create a validator wallet, sign and verify messages and convert a validator wallet to a hex secret key. deps Manage dependencies or multiversx-sdk modules config Configure multiversx-sdk (default values etc.) localnet Set up, start and control localnets @@ -55,10 +55,10 @@ faucet Get xEGLD on Devnet or Testnet $ mxpy contract --help usage: mxpy contract COMMAND [-h] ... -Build, deploy, upgrade and interact with Smart Contracts +Deploy, upgrade and interact with Smart Contracts COMMANDS: - {new,templates,build,clean,test,report,deploy,call,upgrade,query,verify,reproducible-build} + {deploy,call,upgrade,query,verify,reproducible-build,build} OPTIONS: -h, --help show this help message and exit @@ -66,93 +66,13 @@ OPTIONS: ---------------- COMMANDS summary ---------------- -new Create a new Smart Contract project based on a template. -templates List the available Smart Contract templates. -build Build a Smart Contract project. -clean Clean a Smart Contract project. -test Run tests. -report Print a detailed report of the smart contracts. deploy Deploy a Smart Contract. call Interact with a Smart Contract (execute function). upgrade Upgrade a previously-deployed Smart Contract. query Query a Smart Contract (call a pure function) verify Verify the authenticity of the code of a deployed Smart Contract reproducible-build Build a Smart Contract and get the same output as a previously built Smart Contract - -``` -### Contract.New - - -``` -$ mxpy contract new --help -usage: mxpy contract new [-h] ... - -Create a new Smart Contract project based on a template. - -options: - -h, --help show this help message and exit - --name NAME The name of the contract. If missing, the name of the template will be used. - --template TEMPLATE the template to use - --tag TAG the framework version on which the contract should be created - --path PATH the parent directory of the project (default: current directory) - -``` -### Contract.Templates - - -``` -$ mxpy contract templates --help -usage: mxpy contract templates [-h] ... - -List the available Smart Contract templates. - -options: - -h, --help show this help message and exit - --tag TAG the sc-meta framework version referred to - -``` -### Contract.Build - - -``` -$ mxpy contract build --help -usage: mxpy contract build [-h] ... - -Build a Smart Contract project. - -options: - -h, --help show this help message and exit - --path PATH the project directory (default: current directory) - --no-wasm-opt do not optimize wasm files after the build (default: False) - --wasm-symbols for rust projects, does not strip the symbols from the wasm output. Useful for analysing - the bytecode. Creates larger wasm files. Avoid in production (default: False) - --wasm-name WASM_NAME for rust projects, optionally specify the name of the wasm bytecode output file - --wasm-suffix WASM_SUFFIX for rust projects, optionally specify the suffix of the wasm bytecode output file - --target-dir TARGET_DIR for rust projects, forward the parameter to Cargo - --wat also generate a WAT file when building - --mir also emit MIR files when building - --llvm-ir also emit LL (LLVM) files when building - --ignore IGNORE ignore all directories with these names. [default: target] - --no-imports skips extracting the EI imports after building the contracts - --no-abi-git-version skips loading the Git version into the ABI - --twiggy-top generate a twiggy top report after building - --twiggy-paths generate a twiggy paths report after building - --twiggy-monos generate a twiggy monos report after building - --twiggy-dominators generate a twiggy dominators report after building - -``` -### Contract.Clean - - -``` -$ mxpy contract clean --help -usage: mxpy contract clean [-h] ... - -Clean a Smart Contract project. - -options: - -h, --help show this help message and exit - --path PATH the project directory (default: current directory) +build Build a Smart Contract project. This command is DISABLED. ``` ### Contract.Deploy @@ -193,65 +113,60 @@ Output example: } options: - -h, --help show this help message and exit - --bytecode BYTECODE the file containing the WASM bytecode - --abi ABI the ABI of the Smart Contract - --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) - --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) - --metadata-payable ‼ mark the contract as payable (default: not payable) - --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) - --outfile OUTFILE where to save the output (default: stdout) - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --proxy PROXY 🔗 the URL of the proxy - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, - ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 - 0xabba str:TOK-a1c2ef true erd1[..] - --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. - E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] - --wait-result signal to wait for the transaction result - only valid if --send is - set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --bytecode BYTECODE the file containing the WASM bytecode + --abi ABI the ABI file of the Smart Contract + --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) + --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) + --metadata-payable ‼ mark the contract as payable (default: not payable) + --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, + ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 + 0xabba str:TOK-a1c2ef true addr:erd1[..] + --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. + E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### Contract.Call @@ -292,19 +207,19 @@ Output example: } positional arguments: - contract 🖄 the address of the Smart Contract + contract 🖄 the bech32 address of the Smart Contract options: -h, --help show this help message and exit - --abi ABI the ABI of the Smart Contract + --abi ABI the ABI file of the Smart Contract --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction @@ -315,24 +230,13 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian --function FUNCTION the function to call --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 - 0xabba str:TOK-a1c2ef true erd1[..] + 0xabba str:TOK-a1c2ef true addr:erd1[..] --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] --token-transfers TOKEN_TRANSFERS [TOKEN_TRANSFERS ...] @@ -344,16 +248,22 @@ options: set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) - --relay whether to relay the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### Contract.Upgrade @@ -394,68 +304,63 @@ Output example: } positional arguments: - contract 🖄 the address of the Smart Contract + contract 🖄 the bech32 address of the Smart Contract options: - -h, --help show this help message and exit - --abi ABI the ABI of the Smart Contract - --outfile OUTFILE where to save the output (default: stdout) - --bytecode BYTECODE the file containing the WASM bytecode - --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) - --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) - --metadata-payable ‼ mark the contract as payable (default: not payable) - --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --proxy PROXY 🔗 the URL of the proxy - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, - ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 - 0xabba str:TOK-a1c2ef true erd1[..] - --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. - E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] - --wait-result signal to wait for the transaction result - only valid if --send is - set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --abi ABI the ABI file of the Smart Contract + --outfile OUTFILE where to save the output (default: stdout) + --bytecode BYTECODE the file containing the WASM bytecode + --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) + --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) + --metadata-payable ‼ mark the contract as payable (default: not payable) + --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, + ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 + 0xabba str:TOK-a1c2ef true addr:erd1[..] + --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. + E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### Contract.Query @@ -468,58 +373,76 @@ usage: mxpy contract query [-h] ... Query a Smart Contract (call a pure function) positional arguments: - contract 🖄 the address of the Smart Contract + contract 🖄 the bech32 address of the Smart Contract options: -h, --help show this help message and exit - --abi ABI the ABI of the Smart Contract + --abi ABI the ABI file of the Smart Contract --proxy PROXY 🔗 the URL of the proxy --function FUNCTION the function to call --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 0xabba - str:TOK-a1c2ef true erd1[..] + str:TOK-a1c2ef true addr:erd1[..] --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] ``` -### Contract.Report +### Contract.Verify ``` -$ mxpy contract report --help -usage: mxpy contract report [-h] ... +$ mxpy contract verify --help +usage: mxpy contract verify [-h] ... -Print a detailed report of the smart contracts. +Verify the authenticity of the code of a deployed Smart Contract + +positional arguments: + contract 🖄 the bech32 address of the Smart Contract options: - -h, --help show this help message and exit - --skip-build skips the step of building of the wasm contracts - --skip-twiggy skips the steps of building the debug wasm files and running twiggy - --output-format {github-markdown,text-markdown,json} - report output format (default: text-markdown) - --output-file OUTPUT_FILE if specified, the output is written to a file, otherwise it's written - to the standard output - --compare report-1.json [report-2.json ...] create a comparison from two or more reports - --path PATH the project directory (default: current directory) - --no-wasm-opt do not optimize wasm files after the build (default: False) - --wasm-symbols for rust projects, does not strip the symbols from the wasm output. - Useful for analysing the bytecode. Creates larger wasm files. Avoid in - production (default: False) - --wasm-name WASM_NAME for rust projects, optionally specify the name of the wasm bytecode - output file - --wasm-suffix WASM_SUFFIX for rust projects, optionally specify the suffix of the wasm bytecode - output file - --target-dir TARGET_DIR for rust projects, forward the parameter to Cargo - --wat also generate a WAT file when building - --mir also emit MIR files when building - --llvm-ir also emit LL (LLVM) files when building - --ignore IGNORE ignore all directories with these names. [default: target] - --no-imports skips extracting the EI imports after building the contracts - --no-abi-git-version skips loading the Git version into the ABI - --twiggy-top generate a twiggy top report after building - --twiggy-paths generate a twiggy paths report after building - --twiggy-monos generate a twiggy monos report after building - --twiggy-dominators generate a twiggy dominators report after building + -h, --help show this help message and exit + --packaged-src PACKAGED_SRC JSON file containing the source code of the contract + --verifier-url VERIFIER_URL the url of the service that validates the contract + --docker-image DOCKER_IMAGE the docker image used for the build + --contract-variant CONTRACT_VARIANT in case of a multicontract, specify the contract variant you want to verify + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic + or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + +``` +### Contract.ReproducibleBuild + + +``` +$ mxpy contract reproducible-build --help +usage: mxpy contract reproducible-build [-h] ... + +Build a Smart Contract and get the same output as a previously built Smart Contract + +positional arguments: + project the project directory (default: current directory) + +options: + -h, --help show this help message and exit + --debug set debug flag (default: False) + --no-optimization bypass optimizations (for clang) (default: False) + --no-wasm-opt do not optimize wasm files after the build (default: False) + --cargo-target-dir CARGO_TARGET_DIR for rust projects, forward the parameter to Cargo + --wasm-symbols for rust projects, does not strip the symbols from the wasm output. Useful for + analysing the bytecode. Creates larger wasm files. Avoid in production (default: + False) + --wasm-name WASM_NAME for rust projects, optionally specify the name of the wasm bytecode output file + --wasm-suffix WASM_SUFFIX for rust projects, optionally specify the suffix of the wasm bytecode output file + --docker-image DOCKER_IMAGE the docker image tag used to build the contract + --contract CONTRACT contract to build (contract name, as found in Cargo.toml) + --no-docker-interactive + --no-docker-tty + --no-default-platform do not set DOCKER_DEFAULT_PLATFORM environment variable to 'linux/amd64' ``` ## Group **Transactions** @@ -532,7 +455,7 @@ usage: mxpy tx COMMAND [-h] ... Create and broadcast Transactions COMMANDS: - {new,send,get,sign,relay} + {new,send,sign,relay} OPTIONS: -h, --help show this help message and exit @@ -542,7 +465,6 @@ COMMANDS summary ---------------- new Create a new transaction. send Send a previously saved transaction. -get Get a transaction. sign Sign a previously saved transaction. relay Relay a previously saved transaction. @@ -572,12 +494,12 @@ Output example: options: -h, --help show this help message and exit --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender --nonce NONCE # the nonce for the transaction --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -590,20 +512,9 @@ options: --data DATA the payload, or 'memo' of the transaction (default: ) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian --data-file DATA_FILE a file containing transaction data --token-transfers TOKEN_TRANSFERS [TOKEN_TRANSFERS ...] token transfers for transfer & execute, as [token, amount] E.g. @@ -611,17 +522,23 @@ options: --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) - --relay whether to relay the transaction (default: False) --proxy PROXY 🔗 the URL of the proxy + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) --wait-result signal to wait for the transaction result - only valid if --send is set --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is @@ -657,33 +574,98 @@ options: --proxy PROXY 🔗 the URL of the proxy ``` -### Transactions.Get +### Transactions.Sign ``` -$ mxpy tx get --help -usage: mxpy tx get [-h] ... +$ mxpy tx sign --help +usage: mxpy tx sign [-h] ... -Get a transaction. +Sign a previously saved transaction. Output example: =============== { - "transactionOnNetwork": { + "emittedTransaction": { "nonce": 42, "sender": "alice", "receiver": "bob", "...": "..." - } + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" } options: - -h, --help show this help message and exit - --hash HASH the hash - --sender SENDER the sender address - --with-results will also return the results of transaction - --proxy PROXY 🔗 the URL of the proxy - --omit-fields OMIT_FIELDS omit fields in the output payload (default: []) + -h, --help show this help message and exit + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --infile INFILE input file (a previously saved transaction) + --outfile OUTFILE where to save the output (the signed transaction) (default: stdout) + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --proxy PROXY 🔗 the URL of the proxy + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Transactions.Relay + + +``` +$ mxpy tx relay --help +usage: mxpy tx relay [-h] ... + +Relay a previously saved transaction. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" +} + +options: + -h, --help show this help message and exit + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic + or Ledger devices (default: 0) + --infile INFILE input file (a previously saved transaction) + --outfile OUTFILE where to save the output (the relayer signed transaction) (default: + stdout) + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --proxy PROXY 🔗 the URL of the proxy ``` ## Group **Validator** @@ -728,54 +710,49 @@ usage: mxpy validator stake [-h] ... Stake value into the Network options: - -h, --help show this help message and exit - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --reward-address REWARD_ADDRESS the reward address - --validators-file VALIDATORS_FILE a JSON file describing the Nodes - --top-up Stake value for top up + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --reward-address REWARD_ADDRESS the reward address + --validators-pem VALIDATORS_PEM a PEM file describing the nodes; can contain multiple nodes + --top-up Stake value for top up ``` ### Validator.Unstake @@ -788,52 +765,47 @@ usage: mxpy validator unstake [-h] ... Unstake value options: - -h, --help show this help message and exit - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --nodes-public-keys NODES_PUBLIC_KEYS the public keys of the nodes as CSV (addrA,addrB) + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --nodes-public-keys NODES_PUBLIC_KEYS the public keys of the nodes as CSV (addrA,addrB) ``` ### Validator.Unjail @@ -846,52 +818,47 @@ usage: mxpy validator unjail [-h] ... Unjail a Validator Node options: - -h, --help show this help message and exit - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --nodes-public-keys NODES_PUBLIC_KEYS the public keys of the nodes as CSV (addrA,addrB) + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --nodes-public-keys NODES_PUBLIC_KEYS the public keys of the nodes as CSV (addrA,addrB) ``` ### Validator.Unbond @@ -904,52 +871,47 @@ usage: mxpy validator unbond [-h] ... Unbond tokens for a bls key options: - -h, --help show this help message and exit - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --nodes-public-keys NODES_PUBLIC_KEYS the public keys of the nodes as CSV (addrA,addrB) + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --nodes-public-keys NODES_PUBLIC_KEYS the public keys of the nodes as CSV (addrA,addrB) ``` ### Validator.ChangeRewardAddress @@ -962,52 +924,47 @@ usage: mxpy validator change-reward-address [-h] ... Change the reward address options: - -h, --help show this help message and exit - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --reward-address REWARD_ADDRESS the new reward address + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --reward-address REWARD_ADDRESS the new reward address ``` ### Validator.Claim @@ -1020,51 +977,363 @@ usage: mxpy validator claim [-h] ... Claim rewards options: - -h, --help show this help message and exit - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Validator.UnstakeNodes + + +``` +$ mxpy validator unstake-nodes --help +usage: mxpy validator unstake-nodes [-h] ... + +Unstake-nodes will unstake nodes for provided bls keys + +options: + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --nodes-public-keys NODES_PUBLIC_KEYS the public keys of the nodes as CSV (addrA,addrB) + +``` +### Validator.UnstakeTokens + + +``` +$ mxpy validator unstake-tokens --help +usage: mxpy validator unstake-tokens [-h] ... + +This command will un-stake the given amount (if value is greater than the existing topUp value, it will unStake one or several nodes) + +options: + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --unstake-value UNSTAKE_VALUE the unstake value + +``` +### Validator.UnbondNodes + + +``` +$ mxpy validator unbond-nodes --help +usage: mxpy validator unbond-nodes [-h] ... + +It will unBond nodes + +options: + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --nodes-public-keys NODES_PUBLIC_KEYS the public keys of the nodes as CSV (addrA,addrB) + +``` +### Validator.UnbondTokens + + +``` +$ mxpy validator unbond-tokens --help +usage: mxpy validator unbond-tokens [-h] ... + +It will unBond tokens, if provided value is bigger that topUp value will unBond nodes + +options: + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --unbond-value UNBOND_VALUE the unbond value + +``` +### Validator.CleanRegisteredData + + +``` +$ mxpy validator clean-registered-data --help +usage: mxpy validator clean-registered-data [-h] ... + +Deletes duplicated keys from registered data + +options: + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Validator.RestakeUnstakedNodes + + +``` +$ mxpy validator restake-unstaked-nodes --help +usage: mxpy validator restake-unstaked-nodes [-h] ... + +It will reStake UnStaked nodes + +options: + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --nodes-public-keys NODES_PUBLIC_KEYS the public keys of the nodes as CSV (addrA,addrB) ``` ## Group **StakingProvider** @@ -1116,53 +1385,48 @@ usage: mxpy staking-provider create-new-delegation-contract [-h] ... Create a new delegation system smart contract, transferred value must be greater than baseIssuingCost + min deposit value options: - -h, --help show this help message and exit - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --total-delegation-cap TOTAL_DELEGATION_CAP the total delegation contract capacity - --service-fee SERVICE_FEE the delegation contract service fee + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --total-delegation-cap TOTAL_DELEGATION_CAP the total delegation contract capacity + --service-fee SERVICE_FEE the delegation contract service fee ``` ### StakingProvider.GetContractAddress @@ -1177,7 +1441,6 @@ Get create contract address by transaction hash options: -h, --help show this help message and exit --create-tx-hash CREATE_TX_HASH the hash - --sender SENDER the sender address --proxy PROXY 🔗 the URL of the proxy ``` @@ -1191,53 +1454,48 @@ usage: mxpy staking-provider add-nodes [-h] ... Add new nodes must be called by the contract owner options: - -h, --help show this help message and exit - --validators-file VALIDATORS_FILE a JSON file describing the Nodes - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes + --delegation-contract DELEGATION_CONTRACT bech32 address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### StakingProvider.RemoveNodes @@ -1250,54 +1508,49 @@ usage: mxpy staking-provider remove-nodes [-h] ... Remove nodes must be called by the contract owner options: - -h, --help show this help message and exit - --bls-keys BLS_KEYS a list with the bls keys of the nodes - --validators-file VALIDATORS_FILE a JSON file describing the Nodes - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --bls-keys BLS_KEYS a list with the bls keys of the nodes as CSV (addrA,addrB) + --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### StakingProvider.StakeNodes @@ -1310,54 +1563,49 @@ usage: mxpy staking-provider stake-nodes [-h] ... Stake nodes must be called by the contract owner options: - -h, --help show this help message and exit - --bls-keys BLS_KEYS a list with the bls keys of the nodes - --validators-file VALIDATORS_FILE a JSON file describing the Nodes - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --bls-keys BLS_KEYS a list with the bls keys of the nodes as CSV (addrA,addrB) + --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes + --delegation-contract DELEGATION_CONTRACT bech32 address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### StakingProvider.UnbondNodes @@ -1370,54 +1618,49 @@ usage: mxpy staking-provider unbond-nodes [-h] ... Unbond nodes must be called by the contract owner options: - -h, --help show this help message and exit - --bls-keys BLS_KEYS a list with the bls keys of the nodes - --validators-file VALIDATORS_FILE a JSON file describing the Nodes - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --bls-keys BLS_KEYS a list with the bls keys of the nodes as CSV (addrA,addrB) + --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### StakingProvider.UnstakeNodes @@ -1427,58 +1670,53 @@ options: $ mxpy staking-provider unstake-nodes --help usage: mxpy staking-provider unstake-nodes [-h] ... -Unstake nodes must be called by the contract owner - -options: - -h, --help show this help message and exit - --bls-keys BLS_KEYS a list with the bls keys of the nodes - --validators-file VALIDATORS_FILE a JSON file describing the Nodes - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - +Unstake nodes must be called by the contract owner + +options: + -h, --help show this help message and exit + --bls-keys BLS_KEYS a list with the bls keys of the nodes as CSV (addrA,addrB) + --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + ``` ### StakingProvider.UnjailNodes @@ -1490,54 +1728,314 @@ usage: mxpy staking-provider unjail-nodes [-h] ... Unjail nodes must be called by the contract owner options: - -h, --help show this help message and exit - --bls-keys BLS_KEYS a list with the bls keys of the nodes - --validators-file VALIDATORS_FILE a JSON file describing the Nodes - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --bls-keys BLS_KEYS a list with the bls keys of the nodes as CSV (addrA,addrB) + --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### StakingProvider.Delegate + + +``` +$ mxpy staking-provider delegate --help +usage: mxpy staking-provider delegate [-h] ... + +Delegate funds to a delegation contract + +options: + -h, --help show this help message and exit + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### StakingProvider.ClaimRewards + + +``` +$ mxpy staking-provider claim-rewards --help +usage: mxpy staking-provider claim-rewards [-h] ... + +Claim the rewards earned for delegating + +options: + -h, --help show this help message and exit + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### StakingProvider.RedelegateRewards + + +``` +$ mxpy staking-provider redelegate-rewards --help +usage: mxpy staking-provider redelegate-rewards [-h] ... + +Redelegate the rewards earned for delegating + +options: + -h, --help show this help message and exit + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### StakingProvider.Undelegate + + +``` +$ mxpy staking-provider undelegate --help +usage: mxpy staking-provider undelegate [-h] ... + +Undelegate funds from a delegation contract + +options: + -h, --help show this help message and exit + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### StakingProvider.Withdraw + + +``` +$ mxpy staking-provider withdraw --help +usage: mxpy staking-provider withdraw [-h] ... + +Withdraw funds from a delegation contract + +options: + -h, --help show this help message and exit + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### StakingProvider.ChangeServiceFee @@ -1550,53 +2048,48 @@ usage: mxpy staking-provider change-service-fee [-h] ... Change service fee must be called by the contract owner options: - -h, --help show this help message and exit - --service-fee SERVICE_FEE new service fee value - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --service-fee SERVICE_FEE new service fee value + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### StakingProvider.ModifyDelegationCap @@ -1609,53 +2102,48 @@ usage: mxpy staking-provider modify-delegation-cap [-h] ... Modify delegation cap must be called by the contract owner options: - -h, --help show this help message and exit - --delegation-cap DELEGATION_CAP new delegation contract capacity - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --delegation-cap DELEGATION_CAP new delegation contract capacity + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### StakingProvider.AutomaticActivation @@ -1668,54 +2156,49 @@ usage: mxpy staking-provider automatic-activation [-h] ... Automatic activation must be called by the contract owner options: - -h, --help show this help message and exit - --set set automatic activation True - --unset set automatic activation False - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --set set automatic activation True + --unset set automatic activation False + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### StakingProvider.RedelegateCap @@ -1728,54 +2211,49 @@ usage: mxpy staking-provider redelegate-cap [-h] ... Redelegate cap must be called by the contract owner options: - -h, --help show this help message and exit - --set set redelegate cap True - --unset set redelegate cap False - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger + -h, --help show this help message and exit + --set set redelegate cap True + --unset set redelegate cap False + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ### StakingProvider.SetMetadata @@ -1788,95 +2266,106 @@ usage: mxpy staking-provider set-metadata [-h] ... Set metadata must be called by the contract owner options: - -h, --help show this help message and exit - --name NAME name field in staking provider metadata - --website WEBSITE website field in staking provider metadata - --identifier IDENTIFIER identifier field in staking provider metadata - --delegation-contract DELEGATION_CONTRACT address of the delegation contract - --proxy PROXY 🔗 the URL of the proxy - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --nonce NONCE # the nonce for the transaction - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False) - --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) - --gas-limit GAS_LIMIT ⛽ the gas limit - --estimate-gas ⛽ whether to estimate the gas limit (default: 0) - --value VALUE the value to transfer (default: 0) - --chain CHAIN the chain identifier - --version VERSION the transaction version (default: 2) - --relayer RELAYER the bech32 address of the relayer - --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided - --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --relayer-ledger 🔐 bool flag for signing transaction using ledger - --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - --guardian GUARDIAN the address of the guradian - --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service - --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --options OPTIONS the transaction options (default: 0) - --send ✓ whether to broadcast the transaction (default: False) - --simulate whether to simulate the transaction (default: False) - --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) - --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided - --guardian-pem-index GUARDIAN_PEM_INDEX 🔑 the index in the PEM file (default: 0) - --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --guardian-ledger 🔐 bool flag for signing transaction using ledger - --guardian-ledger-account-index GUARDIAN_LEDGER_ACCOUNT_INDEX - 🔐 the index of the account when using Ledger - --guardian-ledger-address-index GUARDIAN_LEDGER_ADDRESS_INDEX - 🔐 the index of the address when using Ledger - -``` -## Group **Account** - - -``` -$ mxpy account --help -usage: mxpy account COMMAND [-h] ... - -Get Account data (nonce, balance) from the Network - -COMMANDS: - {get} - -OPTIONS: - -h, --help show this help message and exit - ----------------- -COMMANDS summary ----------------- -get Query account details (nonce, balance etc.) - -``` -### Account.Get - - -``` -$ mxpy account get --help -usage: mxpy account get [-h] ... - -Query account details (nonce, balance etc.) + -h, --help show this help message and exit + --name NAME name field in staking provider metadata + --website WEBSITE website field in staking provider metadata + --identifier IDENTIFIER identifier field in staking provider metadata + --delegation-contract DELEGATION_CONTRACT address of the delegation contract + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### StakingProvider.MakeDelegationContractFromValidator + + +``` +$ mxpy staking-provider make-delegation-contract-from-validator --help +usage: mxpy staking-provider make-delegation-contract-from-validator [-h] ... + +Create a delegation contract from validator data. Must be called by the node operator options: - -h, --help show this help message and exit - --proxy PROXY 🔗 the URL of the proxy - --address ADDRESS 🖄 the address to query - --balance whether to only fetch the balance - --nonce whether to only fetch the nonce - --username whether to only fetch the username - --omit-fields OMIT_FIELDS omit fields in the output payload (default: []) + -h, --help show this help message and exit + --max-cap MAX_CAP total delegation cap in EGLD, fully denominated. Use value 0 for + uncapped + --fee FEE service fee as hundredths of percents. (e.g. a service fee of 37.45 + percent is expressed by the integer 3745) + --proxy PROXY 🔗 the URL of the proxy + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --nonce NONCE # the nonce for the transaction + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False) + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --outfile OUTFILE where to save the output (signed transaction, hash) (default: stdout) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) ``` ## Group **Wallet** @@ -1964,6 +2453,7 @@ options: -h, --help show this help message and exit --encode whether to encode --decode whether to decode + --hrp HRP the human readable part; only used for encoding to bech32 (default: erd) ``` ### Wallet.SignMessage @@ -1976,16 +2466,16 @@ usage: mxpy wallet sign-message [-h] ... Sign a message options: - -h, --help show this help message and exit - --message MESSAGE the message you want to sign - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender + -h, --help show this help message and exit + --message MESSAGE the message you want to sign + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic + or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender ``` ### Wallet.VerifyMessage @@ -2003,6 +2493,91 @@ options: --message MESSAGE the previously signed message(readable text, as it was signed) --signature SIGNATURE the signature in hex format +``` +## Group **ValidatorWallet** + + +``` +$ mxpy validator-wallet --help +usage: mxpy validator-wallet COMMAND [-h] ... + +Create a validator wallet, sign and verify messages and convert a validator wallet to a hex secret key. + +COMMANDS: + {new,sign-message,verify-message-signature,convert} + +OPTIONS: + -h, --help show this help message and exit + +---------------- +COMMANDS summary +---------------- +new Create a new validator wallet and save it as a PEM file. +sign-message Sign a message. +verify-message-signature Verify a previously signed message. +convert Convert a validator pem file to a hex secret key. + +``` +### Wallet.New + + +``` +$ mxpy validator-wallet new --help +usage: mxpy validator-wallet new [-h] ... + +Create a new validator wallet and save it as a PEM file. + +options: + -h, --help show this help message and exit + --outfile OUTFILE the output path and file name for the generated wallet + +``` +### Wallet.Convert + + +``` +$ mxpy validator-wallet convert --help +usage: mxpy validator-wallet convert [-h] ... + +Convert a validator pem file to a hex secret key. + +options: + -h, --help show this help message and exit + --infile INFILE the pem file of the wallet + --index INDEX the index of the validator in case the file contains multiple validators (default: 0) + +``` +### Wallet.SignMessage + + +``` +$ mxpy validator-wallet sign-message --help +usage: mxpy validator-wallet sign-message [-h] ... + +Sign a message. + +options: + -h, --help show this help message and exit + --message MESSAGE the message you want to sign + --pem PEM the path to a validator pem file + --index INDEX the index of the validator in case the file contains multiple validators (default: 0) + +``` +### Wallet.VerifyMessage + + +``` +$ mxpy validator-wallet verify-message-signature --help +usage: mxpy validator-wallet verify-message-signature [-h] ... + +Verify a previously signed message. + +options: + -h, --help show this help message and exit + --pubkey PUBKEY the hex string representing the validator's public key + --message MESSAGE the previously signed message(readable text, as it was signed) + --signature SIGNATURE the signature in hex format + ``` ## Group **Localnet** @@ -2153,11 +2728,11 @@ usage: mxpy deps install [-h] ... Install dependencies or multiversx-sdk modules. positional arguments: - {all,rust,golang,testwallets} the dependency to install + {all,golang,testwallets} the dependency to install options: - -h, --help show this help message and exit - --overwrite whether to overwrite an existing installation + -h, --help show this help message and exit + --overwrite whether to overwrite an existing installation ``` ### Dependencies.Check @@ -2170,10 +2745,10 @@ usage: mxpy deps check [-h] ... Check whether a dependency is installed. positional arguments: - {all,rust,golang,testwallets} the dependency to check + {all,golang,testwallets} the dependency to check options: - -h, --help show this help message and exit + -h, --help show this help message and exit ``` ## Group **Configuration** @@ -2412,15 +2987,15 @@ usage: mxpy faucet request [-h] ... Request xEGLD. options: - -h, --help show this help message and exit - --pem PEM 🔑 the PEM file, if keyfile not provided - --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) - --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided - --ledger 🔐 bool flag for signing transaction using ledger - --ledger-account-index LEDGER_ACCOUNT_INDEX 🔐 the index of the account when using Ledger - --ledger-address-index LEDGER_ADDRESS_INDEX 🔐 the index of the address when using Ledger - --sender-username SENDER_USERNAME 🖄 the username of the sender - --chain CHAIN the chain identifier + -h, --help show this help message and exit + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic + or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --chain {D,T} the chain identifier ``` diff --git a/CLI.md.sh b/CLI.md.sh index 8bb90601..46ebfb16 100755 --- a/CLI.md.sh +++ b/CLI.md.sh @@ -40,20 +40,18 @@ generate() { code group "Contract" "contract" - command "Contract.New" "contract new" - command "Contract.Templates" "contract templates" - command "Contract.Build" "contract build" - command "Contract.Clean" "contract clean" command "Contract.Deploy" "contract deploy" command "Contract.Call" "contract call" command "Contract.Upgrade" "contract upgrade" command "Contract.Query" "contract query" - command "Contract.Report" "contract report" + command "Contract.Verify" "contract verify" + command "Contract.ReproducibleBuild" "contract reproducible-build" group "Transactions" "tx" command "Transactions.New" "tx new" command "Transactions.Send" "tx send" - command "Transactions.Get" "tx get" + command "Transactions.Sign" "tx sign" + command "Transactions.Relay" "tx relay" group "Validator" "validator" command "Validator.Stake" "validator stake" @@ -62,6 +60,12 @@ generate() { command "Validator.Unbond" "validator unbond" command "Validator.ChangeRewardAddress" "validator change-reward-address" command "Validator.Claim" "validator claim" + command "Validator.UnstakeNodes" "validator unstake-nodes" + command "Validator.UnstakeTokens" "validator unstake-tokens" + command "Validator.UnbondNodes" "validator unbond-nodes" + command "Validator.UnbondTokens" "validator unbond-tokens" + command "Validator.CleanRegisteredData" "validator clean-registered-data" + command "Validator.RestakeUnstakedNodes" "validator restake-unstaked-nodes" group "StakingProvider" "staking-provider" command "StakingProvider.CreateNewDelegationContract" "staking-provider create-new-delegation-contract" @@ -72,14 +76,17 @@ generate() { command "StakingProvider.UnbondNodes" "staking-provider unbond-nodes" command "StakingProvider.UnstakeNodes" "staking-provider unstake-nodes" command "StakingProvider.UnjailNodes" "staking-provider unjail-nodes" + command "StakingProvider.Delegate" "staking-provider delegate" + command "StakingProvider.ClaimRewards" "staking-provider claim-rewards" + command "StakingProvider.RedelegateRewards" "staking-provider redelegate-rewards" + command "StakingProvider.Undelegate" "staking-provider undelegate" + command "StakingProvider.Withdraw" "staking-provider withdraw" command "StakingProvider.ChangeServiceFee" "staking-provider change-service-fee" command "StakingProvider.ModifyDelegationCap" "staking-provider modify-delegation-cap" command "StakingProvider.AutomaticActivation" "staking-provider automatic-activation" command "StakingProvider.RedelegateCap" "staking-provider redelegate-cap" command "StakingProvider.SetMetadata" "staking-provider set-metadata" - - group "Account" "account" - command "Account.Get" "account get" + command "StakingProvider.MakeDelegationContractFromValidator" "staking-provider make-delegation-contract-from-validator" group "Wallet" "wallet" command "Wallet.New" "wallet new" @@ -88,6 +95,12 @@ generate() { command "Wallet.SignMessage" "wallet sign-message" command "Wallet.VerifyMessage" "wallet verify-message" + group "ValidatorWallet" "validator-wallet" + command "Wallet.New" "validator-wallet new" + command "Wallet.Convert" "validator-wallet convert" + command "Wallet.SignMessage" "validator-wallet sign-message" + command "Wallet.VerifyMessage" "validator-wallet verify-message-signature" + group "Localnet" "localnet" command "Localnet.Setup" "localnet setup" command "Localnet.New" "localnet new" diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 7624f43b..381558f1 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -294,7 +294,7 @@ def _add_arguments_arg(sub: Any): "--arguments", nargs="+", help="arguments for the contract transaction, as [number, bech32-address, ascii string, " - "boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 0xabba str:TOK-a1c2ef true erd1[..]", + "boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 0xabba str:TOK-a1c2ef true addr:erd1[..]", ) sub.add_argument( "--arguments-file", diff --git a/pyproject.toml b/pyproject.toml index dfd8f334..02b891b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "multiversx-sdk-cli" -version = "9.12.0" +version = "10.0.0" authors = [ { name="MultiversX" }, ] From cc2f98601084cfeedaba03f7d382de902695906d Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 24 Mar 2025 18:05:46 +0200 Subject: [PATCH 82/84] fix wallet index argument for keystore secret key --- multiversx_sdk_cli/cli_shared.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 00b1743b..26857ffd 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -159,7 +159,7 @@ def add_wallet_args(args: list[str], sub: Any): ) sub.add_argument( "--passfile", - help="🔑 a file containing keyfile's password, if keyfile provided", + help="🔑 a file containing keyfile's password, if keyfile provided. If not provided, you'll be prompted to enter the password.", ) sub.add_argument( "--ledger", @@ -200,7 +200,7 @@ def add_guardian_wallet_args(args: list[str], sub: Any): ) sub.add_argument( "--guardian-passfile", - help="🔑 a file containing keyfile's password, if keyfile provided", + help="🔑 a file containing keyfile's password, if keyfile provided. If not provided, you'll be prompted to enter the password.", ) sub.add_argument( "--guardian-ledger", @@ -221,7 +221,7 @@ def add_relayed_v3_wallet_args(args: list[str], sub: Any): sub.add_argument("--relayer-keyfile", help="🔑 a JSON keyfile, if PEM not provided") sub.add_argument( "--relayer-passfile", - help="🔑 a file containing keyfile's password, if keyfile provided", + help="🔑 a file containing keyfile's password, if keyfile provided. If not provided, you'll be prompted to enter the password.", ) sub.add_argument( "--relayer-ledger", @@ -288,10 +288,12 @@ def prepare_account(args: Any): return Account.new_from_pem(file_path=Path(args.pem), index=args.sender_wallet_index, hrp=hrp) elif args.keyfile: password = load_password(args) + index = args.sender_wallet_index if args.sender_wallet_index != 0 else None + return Account.new_from_keystore( file_path=Path(args.keyfile), password=password, - address_index=args.sender_wallet_index, + address_index=index, hrp=hrp, ) elif args.ledger: @@ -307,10 +309,12 @@ def load_guardian_account(args: Any) -> Union[IAccount, None]: return Account.new_from_pem(file_path=Path(args.guardian_pem), index=args.guardian_wallet_index, hrp=hrp) elif args.guardian_keyfile: password = load_guardian_password(args) + index = args.guardian_wallet_index if args.guardian_wallet_index != 0 else None + return Account.new_from_keystore( file_path=Path(args.guardian_keyfile), password=password, - address_index=args.guardian_wallet_index, + address_index=index, hrp=hrp, ) elif args.guardian_ledger: @@ -436,10 +440,12 @@ def load_relayer_account(args: Any) -> Union[IAccount, None]: return Account.new_from_pem(file_path=Path(args.relayer_pem), index=args.relayer_wallet_index, hrp=hrp) elif args.relayer_keyfile: password = load_relayer_password(args) + index = args.relayer_wallet_index if args.relayer_wallet_index != 0 else None + return Account.new_from_keystore( file_path=Path(args.relayer_keyfile), password=password, - address_index=args.relayer_wallet_index, + address_index=index, hrp=hrp, ) elif args.relayer_ledger: From ce0de18ea03f540e2d70f654db1e7f8ae635093a Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 24 Mar 2025 18:16:28 +0200 Subject: [PATCH 83/84] small contracts refactoring --- multiversx_sdk_cli/cli_contracts.py | 51 +++++++----------------- multiversx_sdk_cli/cli_delegation.py | 22 ----------- multiversx_sdk_cli/contracts.py | 58 +++++++++++----------------- 3 files changed, 37 insertions(+), 94 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 381558f1..cb9df27c 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -350,12 +350,10 @@ def deploy(args: Any): validate_chain_id_args(args) sender = cli_shared.prepare_sender(args) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -379,12 +377,7 @@ def deploy(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) address_computer = AddressComputer(NUMBER_OF_SHARDS) @@ -405,12 +398,10 @@ def call(args: Any): validate_chain_id_args(args) sender = cli_shared.prepare_sender(args) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -433,12 +424,7 @@ def call(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) _send_or_simulate(tx, contract_address, args) @@ -453,12 +439,10 @@ def upgrade(args: Any): validate_chain_id_args(args) sender = cli_shared.prepare_sender(args) - - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) @@ -484,12 +468,7 @@ def upgrade(args: Any): nonce=sender.nonce, version=int(args.version), options=int(args.options), - guardian_account=guardian, - guardian_address=guardian_address, - relayer_account=relayer, - relayer_address=relayer_address, - guardian_service_url=args.guardian_service_url, - guardian_2fa_code=args.guardian_2fa_code, + guardian_and_relayer_data=guardian_and_relayer_data, ) _send_or_simulate(tx, contract_address, args) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 87bb8e07..ab7e27bb 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -394,28 +394,6 @@ def validate_arguments(args: Any): validate_chain_id_args(args) -def prepare_sender(args: Any): - sender = cli_shared.prepare_account(args) - sender.nonce = ( - int(args.nonce) - if args.nonce is not None - else cli_shared.get_current_nonce_for_address(sender.address, args.proxy) - ) - return sender - - -def prepare_guardian(args: Any): - guardian = cli_shared.load_guardian_account(args) - guardian_address = cli_shared.get_guardian_address(guardian, args) - return guardian, guardian_address - - -def prepare_relayer(args: Any): - relayer = cli_shared.load_relayer_account(args) - relayer_address = cli_shared.get_relayer_address(relayer, args) - return relayer, relayer_address - - def _get_delegation_controller(args: Any): chain_id = cli_shared.get_chain_id(args.chain, args.proxy) config = TransactionsFactoryConfig(chain_id) diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 97916a19..a944c224 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -28,6 +28,7 @@ from multiversx_sdk_cli import errors from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.config import get_address_hrp +from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount logger = logging.getLogger("contracts") @@ -73,12 +74,7 @@ def prepare_deploy_transaction( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: @@ -98,16 +94,16 @@ def prepare_deploy_transaction( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -125,12 +121,7 @@ def prepare_execute_transaction( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: token_transfers = self._prepare_token_transfers(transfers) if transfers else [] @@ -150,16 +141,16 @@ def prepare_execute_transaction( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address self.sign_transaction( transaction=tx, sender=caller, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx @@ -180,12 +171,7 @@ def prepare_upgrade_transaction( nonce: int, version: int, options: int, - guardian_account: Optional[IAccount] = None, - guardian_address: Optional[Address] = None, - relayer_account: Optional[IAccount] = None, - relayer_address: Optional[Address] = None, - guardian_service_url: str = "", - guardian_2fa_code: str = "", + guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: @@ -206,16 +192,16 @@ def prepare_upgrade_transaction( tx.nonce = nonce tx.version = version tx.options = options - tx.guardian = guardian_address - tx.relayer = relayer_address + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address self.sign_transaction( transaction=tx, sender=owner, - guardian=guardian_account, - relayer=relayer_account, - guardian_service_url=guardian_service_url, - guardian_2fa_code=guardian_2fa_code, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, ) return tx From 4b2b90c0d58858cffcb33110fd6cb4f27701a72d Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 25 Mar 2025 11:54:45 +0200 Subject: [PATCH 84/84] update publish workflow to publish package to mxpy --- .github/workflows/publish.yml | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 355dde42..c6107254 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,7 +17,7 @@ permissions: contents: read jobs: - deploy: + deploy-mx-sdk-py-cli: runs-on: ubuntu-latest @@ -38,3 +38,32 @@ jobs: with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} + + deploy-mxpy: + runs-on: ubuntu-latest + needs: deploy-mx-sdk-py-cli # Ensure main package is published first + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + + - name: Change package name for mxpy + run: sed -i 's/name = "multiversx-sdk-cli"/name = "mxpy"/' pyproject.toml + + - name: Build and publish mxpy package + run: python -m build + + - name: Publish mxpy package + uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }}