From e694f8eb64a511bd8e43bb5fe6d071eca028863e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 31 Jan 2024 10:09:56 +0200 Subject: [PATCH 01/82] try to add shell completions --- multiversx_sdk_cli/cli.py | 3 +++ requirements.txt | 1 + 2 files changed, 4 insertions(+) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 91ca09d0..8ddd0c02 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -1,9 +1,11 @@ +# PYTHON_ARGCOMPLETE_OK import argparse import logging import sys from argparse import ArgumentParser from typing import Any, List +import argcomplete from rich.logging import RichHandler import multiversx_sdk_cli.cli_accounts @@ -41,6 +43,7 @@ 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) + argcomplete.autocomplete(parser) args = parser.parse_args(argv_with_config_args) if args.verbose: diff --git a/requirements.txt b/requirements.txt index 028aa23d..469cd2a6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ ledgercomm[hid] semver requests-cache rich==13.3.4 +argcomplete multiversx-sdk-core>=0.7.0,<0.8.0 multiversx-sdk-network-providers>=0.12.0,<0.13.0 From 586c267af10e1a781831e5a26026a8d84575fab9 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 31 Jan 2024 10:18:24 +0200 Subject: [PATCH 02/82] add argcomplete in pyproject.toml --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d550edf7..120450f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,8 @@ dependencies = [ "rich==13.3.4", "multiversx-sdk-network-providers>=0.12.0,<0.13.0", "multiversx-sdk-wallet>=0.8.0,<0.9.0,", - "multiversx-sdk-core>=0.7.0,<0.8.0" + "multiversx-sdk-core>=0.7.0,<0.8.0", + "argcomplete" ] [project.scripts] From 905ef7d607df3092f66001c0c57a35117bfbe6e4 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 31 Jan 2024 11:44:06 +0200 Subject: [PATCH 03/82] try performace improvements for shell completions --- multiversx_sdk_cli/cli.py | 10 +++++++--- multiversx_sdk_cli/config.py | 13 ------------- pyproject.toml | 2 +- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 8ddd0c02..66066c5d 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -41,10 +41,14 @@ def main(cli_args: List[str] = sys.argv[1:]): 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) + # argv_with_config_args = config.add_config_args(cli_args) + # parser = setup_parser(argv_with_config_args) + # argcomplete.autocomplete(parser) + # args = parser.parse_args(argv_with_config_args) + + parser = setup_parser(cli_args) argcomplete.autocomplete(parser) - args = parser.parse_args(argv_with_config_args) + args = parser.parse_args(cli_args) if args.verbose: logging.basicConfig(level="DEBUG", force=True, format='%(name)s: %(message)s', handlers=[RichHandler(show_time=False, rich_tracebacks=True)]) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 8db0a4ca..01aee78e 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -202,24 +202,11 @@ def add_config_args(argv: List[str]) -> List[str]: except KeyError: return argv - check_for_deprecated_args(config_args) - final_args = determine_final_args(argv, config_args) print(f"Found extra arguments in mxpy.json. Final arguments: {final_args}") return final_args -def check_for_deprecated_args(args: List[str]) -> None: - if "proxy" in args: - show_warning("Providing `proxy` in the configuration file is deprecated. It will not be used. Please remove it!") - - if "chainID" in args: - show_warning("Providing `chainID` in the configuration file is deprecated. It will not be used. Please remove it!") - - if "txVersion" in args: - show_warning("Providing `txVersion` in the configuration file is deprecated. It will not be used. Please remove it!") - - 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(): diff --git a/pyproject.toml b/pyproject.toml index 120450f3..acefc85d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ dependencies = [ "multiversx-sdk-network-providers>=0.12.0,<0.13.0", "multiversx-sdk-wallet>=0.8.0,<0.9.0,", "multiversx-sdk-core>=0.7.0,<0.8.0", - "argcomplete" + "argcomplete==3.2.2" ] [project.scripts] From 1bedab12a34fdb7445e1a1777cd060a95d5ef3c6 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 31 Jan 2024 11:51:42 +0200 Subject: [PATCH 04/82] undo part of latest commit --- multiversx_sdk_cli/cli.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 66066c5d..8ddd0c02 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -41,14 +41,10 @@ def main(cli_args: List[str] = sys.argv[1:]): 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) - # argcomplete.autocomplete(parser) - # args = parser.parse_args(argv_with_config_args) - - parser = setup_parser(cli_args) + argv_with_config_args = config.add_config_args(cli_args) + parser = setup_parser(argv_with_config_args) argcomplete.autocomplete(parser) - args = parser.parse_args(cli_args) + 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)]) From 037bec1177501640749392cfaef762c771f16702 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 31 Jan 2024 12:02:45 +0200 Subject: [PATCH 05/82] set fixed version for argcomplete --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 469cd2a6..0e46f190 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ ledgercomm[hid] semver requests-cache rich==13.3.4 -argcomplete +argcomplete==3.2.2 multiversx-sdk-core>=0.7.0,<0.8.0 multiversx-sdk-network-providers>=0.12.0,<0.13.0 From 866350c88be28cca6e5eab0442cda1ae8bedd763 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 9 Feb 2024 15:53:23 +0200 Subject: [PATCH 06/82] wrap contract test command to use sc-meta --- multiversx_sdk_cli/cli_contracts.py | 19 +++--- multiversx_sdk_cli/config.py | 4 -- multiversx_sdk_cli/dependencies/install.py | 4 +- multiversx_sdk_cli/dependencies/modules.py | 62 +++---------------- multiversx_sdk_cli/projects/core.py | 15 ++--- multiversx_sdk_cli/projects/project_base.py | 37 +++++------ .../tests/test_cli_contracts.sh | 4 +- multiversx_sdk_cli/tests/test_cli_deps.py | 12 ---- 8 files changed, 44 insertions(+), 113 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 53cdf3db..b659b001 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -53,12 +53,13 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: 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 scenarios (tests).") - _add_project_arg(sub) - sub.add_argument("--directory", default="scenarios", - help="🗀 the directory containing the tests (default: %(default)s)") - sub.add_argument("--wildcard", required=False, help="wildcard to match only specific test files") - _add_recursive_arg(sub) + 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) sub = cli_shared.add_command_subparser(subparsers, "contract", "report", "Print a detailed report of the smart contracts.") @@ -172,7 +173,7 @@ 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)") + help="the project directory (default: current directory)") def _add_build_options_sc_meta(sub: Any): @@ -302,9 +303,7 @@ def do_report(args: Any): def run_tests(args: Any): check_if_rust_is_installed() - project_paths = get_project_paths(args) - for project in project_paths: - projects.run_tests(project, args) + projects.run_tests(args) def deploy(args: Any): diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 01aee78e..5c88f59c 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -143,10 +143,6 @@ def _guard_valid_config_deletion(name: str): 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", - "dependencies.vmtools.urlTemplate.osx": "https://github.com/multiversx/mx-chain-vm-go/archive/{TAG}.tar.gz", - "dependencies.vmtools.urlTemplate.windows": "https://github.com/multiversx/mx-chain-vm-go/archive/{TAG}.tar.gz", "dependencies.rust.tag": "nightly-2023-12-11", "dependencies.golang.resolution": "SDK", "dependencies.golang.tag": "go1.20.7", diff --git a/multiversx_sdk_cli/dependencies/install.py b/multiversx_sdk_cli/dependencies/install.py index 0ca7d900..61bf0fe4 100644 --- a/multiversx_sdk_cli/dependencies/install.py +++ b/multiversx_sdk_cli/dependencies/install.py @@ -5,8 +5,7 @@ from multiversx_sdk_cli import config, errors from multiversx_sdk_cli.dependencies.modules import (DependencyModule, GolangModule, Rust, - TestWalletsModule, - VMToolsModule) + TestWalletsModule) logger = logging.getLogger("install") @@ -51,7 +50,6 @@ def get_all_deps() -> List[DependencyModule]: return [ Rust(key="rust"), GolangModule(key="golang"), - VMToolsModule(key="vmtools"), TestWalletsModule(key="testwallets") ] diff --git a/multiversx_sdk_cli/dependencies/modules.py b/multiversx_sdk_cli/dependencies/modules.py index 3f21dbd2..0638e21c 100644 --- a/multiversx_sdk_cli/dependencies/modules.py +++ b/multiversx_sdk_cli/dependencies/modules.py @@ -135,59 +135,6 @@ def _get_archive_path(self, tag: str) -> Path: return archive -class VMToolsModule(StandaloneModule): - def __init__(self, key: str, aliases: List[str] = []): - super().__init__(key, aliases) - self.repo_name = 'mx-chain-vm-go' - self.organisation = 'multiversx' - - def _post_install(self, tag: str): - dependencies.install_module('golang') - - self.build_binary(tag, 'test') - self.make_binary_symlink_in_parent_folder(tag, 'test', 'run-scenarios') - self.copy_libwasmer_in_parent_directory(tag) - - def build_binary(self, tag: str, binary_name: str): - source_folder = self.binary_source_folder(tag, binary_name) - golang = dependencies.get_module_by_key("golang") - golang_env = golang.get_env() - myprocess.run_process(['go', 'build'], cwd=source_folder, env=golang_env) - - def binary_source_folder(self, tag: str, binary_name: str): - directory = self.get_source_directory(tag) - return directory / 'cmd' / binary_name - - def make_binary_symlink_in_parent_folder(self, tag: str, binary_name: str, symlink_name: str): - source_folder = self.binary_source_folder(tag, binary_name) - binary = source_folder / binary_name - - parent = self.get_parent_directory() - symlink = parent / symlink_name - - symlink.unlink(missing_ok=True) - symlink.symlink_to(binary) - - def copy_libwasmer_in_parent_directory(self, tag: str): - libwasmer_directory = self.get_source_directory(tag) / 'wasmer' - cmd_test_directory = self.get_source_directory(tag) / 'cmd' / 'test' - parent_directory = self.get_parent_directory() - for f in libwasmer_directory.iterdir(): - if f.suffix in ['.dylib', '.so', '.dll']: - # Copy the dynamic library near the "run-scenarios" symlink - shutil.copy(f, parent_directory) - # Though, also copy the dynamic library near the target executable (seems to be necessary on MacOS) - shutil.copy(f, cmd_test_directory) - - def get_env(self) -> Dict[str, str]: - return dict() - - def get_source_directory(self, tag: str) -> Path: - directory = self.get_directory(tag) - first_subdirectory = next(directory.iterdir()) - return first_subdirectory - - class GolangModule(StandaloneModule): def _post_install(self, tag: str): parent_directory = self.get_parent_directory() @@ -244,11 +191,13 @@ 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}") @@ -291,6 +240,7 @@ def install(self, overwrite: bool) -> None: self._install_sc_meta() self._install_wasm_opt() self._install_twiggy() + self._install_sc_meta_deps() def _check_install_env(self, apply_correction: bool = True): """ @@ -359,6 +309,12 @@ def _install_twiggy(self): 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 + 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" diff --git a/multiversx_sdk_cli/projects/core.py b/multiversx_sdk_cli/projects/core.py index ebba16e6..df61f980 100644 --- a/multiversx_sdk_cli/projects/core.py +++ b/multiversx_sdk_cli/projects/core.py @@ -42,17 +42,14 @@ def clean_project(directory: Path): logger.info("Project cleaned.") -def run_tests(project_path: Path, args: Any): - directory = Path(args.directory) - wildcard = args.wildcard +def run_tests(args: Any): + directory = Path(args.path) - logger.info("run_tests.project: %s", project_path) + logger.info("run_tests.project: %s", directory) - dependencies.install_module("vmtools") - - guards.is_directory(project_path) - project = load_project(project_path) - project.run_tests(directory, wildcard) + guards.is_directory(directory) + project = load_project(directory) + project.run_tests(args) def get_project_paths_recursively(base_path: Path) -> List[Path]: diff --git a/multiversx_sdk_cli/projects/project_base.py b/multiversx_sdk_cli/projects/project_base.py index 74777af1..9e075382 100644 --- a/multiversx_sdk_cli/projects/project_base.py +++ b/multiversx_sdk_cli/projects/project_base.py @@ -1,13 +1,11 @@ -import glob import logging import shutil from abc import abstractmethod from os import path from pathlib import Path -from typing import Any, Dict, List, Union, cast, final +from typing import Any, Dict, List, Union, final from multiversx_sdk_cli import dependencies, errors, myprocess, utils -from multiversx_sdk_cli.dependencies.modules import StandaloneModule 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 @@ -129,23 +127,22 @@ def ensure_config_file(self) -> None: def default_config(self) -> Dict[str, Any]: return dict() - def run_tests(self, tests_directory: Path, wildcard: str = ""): - vmtools = cast(StandaloneModule, dependencies.get_module_by_key("vmtools")) - tool_env = vmtools.get_env() - tool = path.join(vmtools.get_parent_directory(), "run-scenarios") - test_folder = self.directory / tests_directory - - if not wildcard: - args = [tool, str(test_folder)] - myprocess.run_process(args, env=tool_env) - else: - pattern = test_folder / wildcard - test_files = glob.glob(str(pattern)) - - for test_file in test_files: - print("Run test for:", test_file) - args = [tool, test_file] - myprocess.run_process(args, env=tool_env) + def run_tests(self, args: Any): + command = ["sc-meta", "test"] + + if args.path: + command.extend(["--path", str(args.path)]) + + 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]: diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.sh b/multiversx_sdk_cli/tests/test_cli_contracts.sh index fe980cfe..bf7f9fa6 100755 --- a/multiversx_sdk_cli/tests/test_cli_contracts.sh +++ b/multiversx_sdk_cli/tests/test_cli_contracts.sh @@ -36,8 +36,8 @@ testBuildContracts() { testRunScenarios() { echo "testRunScenarios" - ${CLI} --verbose contract test --directory="scenarios" ${SANDBOX}/adder || return 1 - ${CLI} --verbose contract test --directory="scenarios" ${SANDBOX}/empty || return 1 + ${CLI} --verbose contract test --path=${SANDBOX}/adder || return 1 + ${CLI} --verbose contract test --path=${SANDBOX}/empty || return 1 } testWasmName() { diff --git a/multiversx_sdk_cli/tests/test_cli_deps.py b/multiversx_sdk_cli/tests/test_cli_deps.py index 762e3d24..63162d71 100644 --- a/multiversx_sdk_cli/tests/test_cli_deps.py +++ b/multiversx_sdk_cli/tests/test_cli_deps.py @@ -31,18 +31,6 @@ def test_deps_check_rust(): assert which_twiggy and Path.is_file(Path(which_twiggy)) -@pytest.mark.skip_on_windows -def test_deps_install_vmtools(): - return_code = main(["deps", "install", "vmtools"]) - assert return_code == 0 - - -@pytest.mark.skip_on_windows -def test_deps_check_vmtools(): - return_code = main(["deps", "check", "vmtools"]) - assert return_code == 0 - - def test_deps_install_testwallets(): return_code = main(["deps", "install", "testwallets"]) assert return_code == 0 From c1afaa3e79cf167817336eb316dad65a5f1173bb Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 12 Feb 2024 13:16:05 +0200 Subject: [PATCH 07/82] remove unused function --- multiversx_sdk_cli/cli_contracts.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index b659b001..036936e5 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -214,10 +214,6 @@ def _add_build_options_args(sub: Any): help="for rust projects, optionally specify the suffix of the wasm bytecode output file") -def _add_recursive_arg(sub: Any): - sub.add_argument("-r", "--recursive", dest="recursive", action="store_true", help="locate projects recursively") - - def _add_bytecode_arg(sub: Any): sub.add_argument("--bytecode", type=str, required=True, help="the file containing the WASM bytecode") From d80273f1cf982f23c95fc8bddab031c8c1ae005f Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 12 Feb 2024 15:05:32 +0200 Subject: [PATCH 08/82] skip installation of sc-meta deps if os is windows --- multiversx_sdk_cli/dependencies/modules.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/multiversx_sdk_cli/dependencies/modules.py b/multiversx_sdk_cli/dependencies/modules.py index 0638e21c..797e177b 100644 --- a/multiversx_sdk_cli/dependencies/modules.py +++ b/multiversx_sdk_cli/dependencies/modules.py @@ -1,5 +1,6 @@ import logging import os +import platform import shutil from os import path from pathlib import Path @@ -311,6 +312,11 @@ def _install_twiggy(self): def _install_sc_meta_deps(self): # this is needed for sc-meta to run the tests + # if os is Windows skip insallation + os = platform.system().lower() + if os == "windows": + return + logger.info("Installing sc-meta dependencies.") args = ["sc-meta", "install", "all"] myprocess.run_process(args) From ffe10345e32b9895e6b7686ad4e7f0ca27418176 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 12 Feb 2024 15:43:02 +0200 Subject: [PATCH 09/82] expand user path --- multiversx_sdk_cli/projects/project_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/projects/project_base.py b/multiversx_sdk_cli/projects/project_base.py index 9e075382..75d7db8b 100644 --- a/multiversx_sdk_cli/projects/project_base.py +++ b/multiversx_sdk_cli/projects/project_base.py @@ -131,7 +131,7 @@ def run_tests(self, args: Any): command = ["sc-meta", "test"] if args.path: - command.extend(["--path", str(args.path)]) + command.extend(["--path", str(Path(args.path).expanduser())]) if args.go: command.append("--go") From 0fbb8724ce8c412b76eb591390a875ff8fb0fb44 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 12 Mar 2024 13:10:03 +0200 Subject: [PATCH 10/82] implement a native auth client and faucet functionality --- CLI.md | 51 ++++++- CLI.md.sh | 3 + multiversx_sdk_cli/cli.py | 2 + multiversx_sdk_cli/cli_faucet.py | 76 +++++++++++ multiversx_sdk_cli/errors.py | 5 + multiversx_sdk_cli/native_auth_client.py | 101 ++++++++++++++ .../tests/test_native_auth_client.py | 128 ++++++++++++++++++ 7 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 multiversx_sdk_cli/cli_faucet.py create mode 100644 multiversx_sdk_cli/native_auth_client.py create mode 100644 multiversx_sdk_cli/tests/test_native_auth_client.py diff --git a/CLI.md b/CLI.md index e3721cbb..257acf69 100644 --- a/CLI.md +++ b/CLI.md @@ -23,7 +23,7 @@ See: COMMAND GROUPS: - {contract,tx,validator,account,ledger,wallet,deps,config,localnet,data,staking-provider,dns} + {contract,tx,validator,account,ledger,wallet,deps,config,localnet,data,staking-provider,dns,faucet} TOP-LEVEL OPTIONS: -h, --help show this help message and exit @@ -45,6 +45,7 @@ localnet Set up, start and control localnets data Data manipulation omnitool staking-provider Staking provider omnitool dns Operations related to the Domain Name Service +faucet Get xEGLD on Devnet or Testnet ``` ## Group **Contract** @@ -1093,6 +1094,7 @@ 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 @@ -1142,6 +1144,7 @@ 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 @@ -1191,6 +1194,7 @@ 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 @@ -1240,6 +1244,7 @@ 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 @@ -1289,6 +1294,7 @@ 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 @@ -2116,3 +2122,46 @@ options: --use-global use the global storage (default: False) ``` +## Group **Faucet** + + +``` +$ mxpy faucet --help +usage: mxpy faucet COMMAND [-h] ... + +Get xEGLD on Devnet or Testnet + +COMMANDS: + {request} + +OPTIONS: + -h, --help show this help message and exit + +---------------- +COMMANDS summary +---------------- +request Request xEGLD. + +``` +### Faucet.Request + + +``` +$ mxpy faucet request --help +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 + +``` diff --git a/CLI.md.sh b/CLI.md.sh index 49790861..8bb90601 100755 --- a/CLI.md.sh +++ b/CLI.md.sh @@ -114,6 +114,9 @@ generate() { command "Data.Dump" "data parse" command "Data.Store" "data store" command "Data.Load" "data load" + + group "Faucet" "faucet" + command "Faucet.Request" "faucet request" } generate diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 91ca09d0..2804430f 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -13,6 +13,7 @@ import multiversx_sdk_cli.cli_delegation import multiversx_sdk_cli.cli_deps import multiversx_sdk_cli.cli_dns +import multiversx_sdk_cli.cli_faucet import multiversx_sdk_cli.cli_ledger import multiversx_sdk_cli.cli_localnet import multiversx_sdk_cli.cli_transactions @@ -97,6 +98,7 @@ def setup_parser(args: List[str]): commands.append(multiversx_sdk_cli.cli_data.setup_parser(subparsers)) commands.append(multiversx_sdk_cli.cli_delegation.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_dns.setup_parser(args, subparsers)) + commands.append(multiversx_sdk_cli.cli_faucet.setup_parser(args, subparsers)) parser.epilog = """ ---------------------- diff --git a/multiversx_sdk_cli/cli_faucet.py b/multiversx_sdk_cli/cli_faucet.py new file mode 100644 index 00000000..252970af --- /dev/null +++ b/multiversx_sdk_cli/cli_faucet.py @@ -0,0 +1,76 @@ +import logging +import webbrowser +from enum import Enum +from typing import Any, List, Tuple + +from multiversx_sdk_core import Message, MessageComputer + +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") + + +class WebWalletUrls(Enum): + DEVNET = "https://devnet-wallet.multiversx.com" + TESTNET = "https://testnet-wallet.multiversx.com" + + +class ApiUrls(Enum): + DEVNET = "https://devnet-api.multiversx.com" + TESTNET = "https://testnet-api.multiversx.com" + + +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.set_defaults(func=faucet) + + parser.epilog = cli_shared.build_group_epilog(subparsers) + return subparsers + + +def faucet(args: Any): + account = cli_shared.prepare_account(args) + wallet, api = get_wallet_and_api_urls(args) + + config = NativeAuthClientConfig(origin=wallet, api_url=api) + client = NativeAuthClient(config) + + init_token = client.initialize() + message = Message(f"{account.address.to_bech32()}{init_token}".encode()) + + message_computer = MessageComputer() + signature = account.sign_message(message_computer.compute_bytes_for_signing(message)) + + 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) + + +def call_web_Wallet_faucet(wallet_url: str, access_token: str): + faucet_url = f"{wallet_url}/faucet?accessToken={access_token}" + webbrowser.open_new_tab(faucet_url) + + +def get_wallet_and_api_urls(args: Any) -> Tuple[str, str]: + chain: str = args.chain + + if chain.upper() == "D": + return WebWalletUrls.DEVNET.value, ApiUrls.DEVNET.value + + if chain.upper() == "T": + return WebWalletUrls.TESTNET.value, ApiUrls.TESTNET.value + + raise BadUserInput("Invalid chain id. Choose between 'D' for devnet and 'T' for testnet.") diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 13b06e87..7f834e8a 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -208,3 +208,8 @@ def __init__(self, message: str, url: str, data: str, code: str): "code": code } super().__init__(message, inner) + + +class NativeAuthClientError(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 new file mode 100644 index 00000000..6cba3010 --- /dev/null +++ b/multiversx_sdk_cli/native_auth_client.py @@ -0,0 +1,101 @@ +import base64 +import json +from typing import Any, Dict, Optional + +import requests + +from multiversx_sdk_cli.errors import NativeAuthClientError + + +class NativeAuthClientConfig: + def __init__( + self, + origin: str = '', + api_url: str = "https://api.multiversx.com", + expiry_seconds: int = 60 * 60 * 24, + 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: NativeAuthClientConfig = NativeAuthClientConfig()) -> None: + self.config = config + + 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 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_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 = response["data"]["blocks"] + block = [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 = 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) + 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 = 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 new file mode 100644 index 00000000..3eaadd16 --- /dev/null +++ b/multiversx_sdk_cli/tests/test_native_auth_client.py @@ -0,0 +1,128 @@ +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) + 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) + 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) + 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 From 4bfd7b7af52a8d2abdd972a9c04e622f506680da Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 12 Mar 2024 13:27:24 +0200 Subject: [PATCH 11/82] update dev dependencies --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index 85d17fda..ce2c47ff 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,4 @@ pytest flake8 autopep8 +pytest-mock From 41e1a562c55c1ded582a1a965910048176d703aa Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 12 Mar 2024 13:41:39 +0200 Subject: [PATCH 12/82] install pytest-mock in GH action --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 012b4f9f..c563d166 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,7 @@ jobs: python3 -m pip install --upgrade pip pip3 install -r requirements.txt pip3 install pytest + pip3 install pytest-mock - name: Set github_api_token run: | mkdir ~/multiversx-sdk From e0023e9026426705ffd2869d179500b1959e365a Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 12 Mar 2024 13:59:50 +0200 Subject: [PATCH 13/82] install requirements-dev in GH action --- Dockerfile | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..1a8d01d0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +FROM ubuntu:22.04 + +ARG USERNAME=developer +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +# Create the user +RUN groupadd --gid $USER_GID $USERNAME \ + && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \ + # + # [Optional] Add sudo support. Omit if you don't need to install software after connecting. + && apt-get update \ + && apt-get install -y sudo \ + && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ + && chmod 0440 /etc/sudoers.d/$USERNAME + +# Install some dependencies as root +RUN apt-get update && apt-get install -y \ + wget \ + python3.10 python3-pip python3.10-venv \ + git \ + pkg-config \ + libssl-dev && \ + rm -rf /var/lib/apt/lists/* + +# Switch to regular user +USER $USERNAME +WORKDIR /home/${USERNAME} + +RUN sudo apt-get update +RUN sudo apt-get install git + +# RUN sudo apt install pipx -y 8 + +# RUN pipx ensurepath + +# RUN pipx install git+https://github.com/multiversx/mx-sdk-py-cli@fix-deps-all From 50c81f3b8a3f828dbf56baef9639110e20a6f592 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 12 Mar 2024 14:02:15 +0200 Subject: [PATCH 14/82] install pytes-mock for windows workflow --- .github/workflows/build-windows.yml | 1 + .github/workflows/build.yml | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index e800b693..9a84e53b 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -31,6 +31,7 @@ jobs: python3 -m pip install --upgrade pip pip3 install -r requirements.txt pip3 install pytest + pip3 install pytest-mock - name: Set github_api_token shell: bash run: | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c563d166..9dda9a37 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,8 +29,7 @@ jobs: run: | python3 -m pip install --upgrade pip pip3 install -r requirements.txt - pip3 install pytest - pip3 install pytest-mock + pip3 install -r requirements-dev.txt - name: Set github_api_token run: | mkdir ~/multiversx-sdk From 046adc28d9827f066dc250ba38acafaf57853154 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 13 Mar 2024 11:53:25 +0200 Subject: [PATCH 15/82] fixes after review --- Dockerfile | 37 ------------------------ multiversx_sdk_cli/native_auth_client.py | 6 +++- 2 files changed, 5 insertions(+), 38 deletions(-) delete mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 1a8d01d0..00000000 --- a/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM ubuntu:22.04 - -ARG USERNAME=developer -ARG USER_UID=1000 -ARG USER_GID=$USER_UID - -# Create the user -RUN groupadd --gid $USER_GID $USERNAME \ - && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \ - # - # [Optional] Add sudo support. Omit if you don't need to install software after connecting. - && apt-get update \ - && apt-get install -y sudo \ - && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ - && chmod 0440 /etc/sudoers.d/$USERNAME - -# Install some dependencies as root -RUN apt-get update && apt-get install -y \ - wget \ - python3.10 python3-pip python3.10-venv \ - git \ - pkg-config \ - libssl-dev && \ - rm -rf /var/lib/apt/lists/* - -# Switch to regular user -USER $USERNAME -WORKDIR /home/${USERNAME} - -RUN sudo apt-get update -RUN sudo apt-get install git - -# RUN sudo apt install pipx -y 8 - -# RUN pipx ensurepath - -# RUN pipx install git+https://github.com/multiversx/mx-sdk-py-cli@fix-deps-all diff --git a/multiversx_sdk_cli/native_auth_client.py b/multiversx_sdk_cli/native_auth_client.py index 6cba3010..db9c8217 100644 --- a/multiversx_sdk_cli/native_auth_client.py +++ b/multiversx_sdk_cli/native_auth_client.py @@ -6,13 +6,17 @@ from multiversx_sdk_cli.errors import NativeAuthClientError +NUMBER_OF_SECONDS_IN_A_MINUTE = 60 +NUMBER_OF_MINUTES_IN_AN_HOUR = 60 +NUMBER_OF_HOURS_IN_A_DAY = 24 + class NativeAuthClientConfig: def __init__( self, origin: str = '', api_url: str = "https://api.multiversx.com", - expiry_seconds: int = 60 * 60 * 24, + expiry_seconds: int = NUMBER_OF_SECONDS_IN_A_MINUTE * NUMBER_OF_MINUTES_IN_AN_HOUR * NUMBER_OF_HOURS_IN_A_DAY, block_hash_shard: Optional[int] = None, gateway_url: Optional[str] = None, extra_request_headers: Optional[Dict[str, str]] = None From 63627d02674fed51398139504c84a7c8cdd46ba9 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 13 Mar 2024 13:50:20 +0200 Subject: [PATCH 16/82] create constants for default values --- multiversx_sdk_cli/native_auth_client.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/multiversx_sdk_cli/native_auth_client.py b/multiversx_sdk_cli/native_auth_client.py index db9c8217..f5c48372 100644 --- a/multiversx_sdk_cli/native_auth_client.py +++ b/multiversx_sdk_cli/native_auth_client.py @@ -6,17 +6,16 @@ from multiversx_sdk_cli.errors import NativeAuthClientError -NUMBER_OF_SECONDS_IN_A_MINUTE = 60 -NUMBER_OF_MINUTES_IN_AN_HOUR = 60 -NUMBER_OF_HOURS_IN_A_DAY = 24 +EXPIRY_TIME_IN_SECONDS = 60 * 60 * 24 +DEFAULT_API_URL = "https://api.multiversx.com" class NativeAuthClientConfig: def __init__( self, origin: str = '', - api_url: str = "https://api.multiversx.com", - expiry_seconds: int = NUMBER_OF_SECONDS_IN_A_MINUTE * NUMBER_OF_MINUTES_IN_AN_HOUR * NUMBER_OF_HOURS_IN_A_DAY, + api_url: str = DEFAULT_API_URL, + expiry_seconds: int = EXPIRY_TIME_IN_SECONDS, block_hash_shard: Optional[int] = None, gateway_url: Optional[str] = None, extra_request_headers: Optional[Dict[str, str]] = None From 574bc98b60a6a769d27a43e88d91bc4c5fcfd5bb Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 10 Jun 2024 14:29:22 +0300 Subject: [PATCH 17/82] integrate new sdk-py package --- multiversx_sdk_cli/accounts.py | 7 +++---- multiversx_sdk_cli/cli_accounts.py | 4 +--- multiversx_sdk_cli/cli_contracts.py | 6 ++---- multiversx_sdk_cli/cli_delegation.py | 4 +--- multiversx_sdk_cli/cli_dns.py | 4 +--- multiversx_sdk_cli/cli_output.py | 6 +++--- multiversx_sdk_cli/cli_shared.py | 4 +--- multiversx_sdk_cli/cli_wallet.py | 6 ++---- multiversx_sdk_cli/config.py | 1 - multiversx_sdk_cli/contract_verification.py | 2 +- multiversx_sdk_cli/contracts.py | 14 +++++++------- multiversx_sdk_cli/cosign_transaction.py | 6 +++--- multiversx_sdk_cli/custom_network_provider.py | 2 +- multiversx_sdk_cli/delegation/staking_provider.py | 6 ++---- multiversx_sdk_cli/dns.py | 2 +- multiversx_sdk_cli/localnet/wallets.py | 2 +- .../projects/report/data/folder_report.py | 10 +++++++--- multiversx_sdk_cli/sign_verify.py | 3 +-- multiversx_sdk_cli/tests/test_cli_shared.py | 2 +- multiversx_sdk_cli/tests/test_cli_wallet.py | 4 ++-- multiversx_sdk_cli/tests/test_contracts.py | 2 +- multiversx_sdk_cli/tests/test_proxy.py | 4 +--- multiversx_sdk_cli/tests/test_validators_core.py | 2 +- multiversx_sdk_cli/transactions.py | 2 +- multiversx_sdk_cli/utils.py | 15 +-------------- multiversx_sdk_cli/validators/core.py | 4 +--- multiversx_sdk_cli/validators/validators_file.py | 4 +--- pyproject.toml | 4 +--- requirements.txt | 4 +--- 29 files changed, 50 insertions(+), 86 deletions(-) diff --git a/multiversx_sdk_cli/accounts.py b/multiversx_sdk_cli/accounts.py index 515093b1..489f03a7 100644 --- a/multiversx_sdk_cli/accounts.py +++ b/multiversx_sdk_cli/accounts.py @@ -2,10 +2,9 @@ from pathlib import Path from typing import Any, Optional, Protocol -from multiversx_sdk_core import (Address, Message, MessageComputer, - TransactionComputer) -from multiversx_sdk_network_providers.accounts import AccountOnNetwork -from multiversx_sdk_wallet import UserSigner +from multiversx_sdk import (Address, Message, MessageComputer, + TransactionComputer, UserSigner) +from multiversx_sdk.network_providers.accounts import AccountOnNetwork from multiversx_sdk_cli.constants import DEFAULT_HRP from multiversx_sdk_cli.interfaces import IAccount, IAddress, ITransaction diff --git a/multiversx_sdk_cli/cli_accounts.py b/multiversx_sdk_cli/cli_accounts.py index 1ebf8bc6..9ae5933a 100644 --- a/multiversx_sdk_cli/cli_accounts.py +++ b/multiversx_sdk_cli/cli_accounts.py @@ -1,9 +1,7 @@ import logging from typing import Any -from multiversx_sdk_core import Address -from multiversx_sdk_network_providers.proxy_network_provider import \ - ProxyNetworkProvider +from multiversx_sdk import Address, ProxyNetworkProvider from multiversx_sdk_cli import cli_shared, utils diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index c6eda82a..62cc983b 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -3,10 +3,8 @@ from pathlib import Path from typing import Any, List -from multiversx_sdk_core import Address, AddressComputer, Transaction -from multiversx_sdk_core.transaction_factories import TransactionsFactoryConfig -from multiversx_sdk_network_providers.proxy_network_provider import \ - ProxyNetworkProvider +from multiversx_sdk import (Address, AddressComputer, ProxyNetworkProvider, + Transaction, TransactionsFactoryConfig) from multiversx_sdk_cli import cli_shared, projects, utils from multiversx_sdk_cli.cli_output import CLIOutputBuilder diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 7d936c18..b5601ed8 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -1,8 +1,6 @@ from typing import Any, List -from multiversx_sdk_core.transaction_factories import TransactionsFactoryConfig -from multiversx_sdk_network_providers.proxy_network_provider import \ - ProxyNetworkProvider +from multiversx_sdk import ProxyNetworkProvider, TransactionsFactoryConfig from multiversx_sdk_cli import cli_shared, errors, utils from multiversx_sdk_cli.delegation import DelegationOperations diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index 3e270595..a7c8dcd6 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -1,8 +1,6 @@ from typing import Any, List -from multiversx_sdk_core import Address -from multiversx_sdk_network_providers.proxy_network_provider import \ - ProxyNetworkProvider +from multiversx_sdk import Address, ProxyNetworkProvider from prettytable import PrettyTable from multiversx_sdk_cli import cli_shared diff --git a/multiversx_sdk_cli/cli_output.py b/multiversx_sdk_cli/cli_output.py index 91560bfd..bb2fdc94 100644 --- a/multiversx_sdk_cli/cli_output.py +++ b/multiversx_sdk_cli/cli_output.py @@ -3,8 +3,7 @@ from collections import OrderedDict from typing import Any, Dict, List, Optional, Union -from multiversx_sdk_network_providers.transactions import \ - transaction_to_dictionary +from multiversx_sdk import TransactionsConverter from multiversx_sdk_cli import utils from multiversx_sdk_cli.interfaces import IAddress, ITransaction @@ -52,7 +51,8 @@ def build(self) -> Dict[str, Any]: output: Dict[str, Any] = OrderedDict() if self.emitted_transaction: - emitted_transaction_dict = transaction_to_dictionary(self.emitted_transaction) + tx_converter = TransactionsConverter() + emitted_transaction_dict = tx_converter.transaction_to_dictionary(self.emitted_transaction) emitted_transaction_hash = self.emitted_transaction_hash or "" emitted_transaction_data = self.emitted_transaction.data.decode() utils.omit_fields(emitted_transaction_dict, self.emitted_transaction_omitted_fields) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 48d04f2f..10ba00fe 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -5,9 +5,7 @@ from argparse import FileType from typing import Any, Dict, List, Text, cast -from multiversx_sdk_core import Address -from multiversx_sdk_network_providers.proxy_network_provider import \ - ProxyNetworkProvider +from multiversx_sdk import Address, ProxyNetworkProvider from multiversx_sdk_cli import config, errors, utils from multiversx_sdk_cli.accounts import Account, LedgerAccount diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index de7dd7ce..9a71f8d1 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -5,10 +5,8 @@ from pathlib import Path from typing import Any, List, Optional, Tuple -from multiversx_sdk_core import Address -from multiversx_sdk_wallet import UserSecretKey, UserWallet -from multiversx_sdk_wallet.mnemonic import Mnemonic -from multiversx_sdk_wallet.user_pem import UserPEM +from multiversx_sdk import (Address, Mnemonic, UserPEM, UserSecretKey, + UserWallet) from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.constants import DEFAULT_HRP diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 614857db..1ed612fc 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -3,7 +3,6 @@ from typing import Any, Dict, List from multiversx_sdk_cli import errors, utils -from multiversx_sdk_cli.ux import show_warning SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() LOCAL_CONFIG_PATH = Path("mxpy.json").resolve() diff --git a/multiversx_sdk_cli/contract_verification.py b/multiversx_sdk_cli/contract_verification.py index 7c6266da..01e833bb 100644 --- a/multiversx_sdk_cli/contract_verification.py +++ b/multiversx_sdk_cli/contract_verification.py @@ -6,7 +6,7 @@ from typing import Any, Dict, Optional, Tuple import requests -from multiversx_sdk_core import Address +from multiversx_sdk import Address from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.errors import KnownError diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index a0a18855..515dec52 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -3,12 +3,10 @@ from pathlib import Path from typing import Any, List, Optional, Protocol, Sequence, Union -from multiversx_sdk_core import (Token, TokenComputer, TokenTransfer, - Transaction, TransactionPayload) -from multiversx_sdk_core.address import Address -from multiversx_sdk_core.transaction_factories import \ - SmartContractTransactionsFactory -from multiversx_sdk_network_providers.interface import IContractQuery +from multiversx_sdk import (Address, SmartContractTransactionsFactory, Token, + TokenComputer, TokenTransfer, Transaction, + TransactionPayload) +from multiversx_sdk.network_providers.interface import IContractQuery from multiversx_sdk_cli import errors from multiversx_sdk_cli.accounts import Account @@ -70,11 +68,13 @@ class IConfig(Protocol): chain_id: str min_gas_limit: int gas_limit_per_byte: int + gas_limit_claim_developer_rewards: int + gas_limit_change_owner_address: int class SmartContract: def __init__(self, config: IConfig): - self._factory = SmartContractTransactionsFactory(config, TokenComputer()) + self._factory = SmartContractTransactionsFactory(config) def prepare_deploy_transaction(self, owner: Account, diff --git a/multiversx_sdk_cli/cosign_transaction.py b/multiversx_sdk_cli/cosign_transaction.py index 30a3ec79..fce9e274 100644 --- a/multiversx_sdk_cli/cosign_transaction.py +++ b/multiversx_sdk_cli/cosign_transaction.py @@ -1,17 +1,17 @@ from typing import Any, Dict import requests -from multiversx_sdk_network_providers.transactions import \ - transaction_to_dictionary +from multiversx_sdk import TransactionsConverter from multiversx_sdk_cli.errors import GuardianServiceError from multiversx_sdk_cli.interfaces import ITransaction def cosign_transaction(transaction: ITransaction, service_url: str, guardian_code: str) -> ITransaction: + tx_converter = TransactionsConverter() payload = { "code": f"{guardian_code}", - "transaction": transaction_to_dictionary(transaction) + "transaction": tx_converter.transaction_to_dictionary(transaction) } url = f"{service_url}/sign-transaction" diff --git a/multiversx_sdk_cli/custom_network_provider.py b/multiversx_sdk_cli/custom_network_provider.py index 3354a495..e11c9870 100644 --- a/multiversx_sdk_cli/custom_network_provider.py +++ b/multiversx_sdk_cli/custom_network_provider.py @@ -1,6 +1,6 @@ from typing import Any, Dict, Optional, Protocol -from multiversx_sdk_network_providers import GenericError, ProxyNetworkProvider +from multiversx_sdk import GenericError, ProxyNetworkProvider from multiversx_sdk_cli.errors import ProxyError from multiversx_sdk_cli.interfaces import ISimulateResponse, ITransaction diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation/staking_provider.py index 3152925e..5b9de82c 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -1,10 +1,8 @@ from pathlib import Path from typing import Any, List, Protocol, Tuple -from multiversx_sdk_core import Address -from multiversx_sdk_core.transaction_factories import \ - DelegationTransactionsFactory -from multiversx_sdk_wallet import ValidatorPublicKey +from multiversx_sdk import (Address, DelegationTransactionsFactory, + ValidatorPublicKey) from multiversx_sdk_cli.accounts import Account, LedgerAccount from multiversx_sdk_cli.cli_password import load_password diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 54485a48..26fce0b9 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -1,7 +1,7 @@ from typing import Any, List, Protocol from Cryptodome.Hash import keccak -from multiversx_sdk_core import Address, AddressComputer +from multiversx_sdk import Address, AddressComputer from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.accounts import Account diff --git a/multiversx_sdk_cli/localnet/wallets.py b/multiversx_sdk_cli/localnet/wallets.py index 2134e889..f4e3e5e2 100644 --- a/multiversx_sdk_cli/localnet/wallets.py +++ b/multiversx_sdk_cli/localnet/wallets.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import Dict, Tuple -from multiversx_sdk_wallet.validator_pem import ValidatorPEM +from multiversx_sdk import ValidatorPEM from multiversx_sdk_cli import errors, utils from multiversx_sdk_cli.accounts import Account diff --git a/multiversx_sdk_cli/projects/report/data/folder_report.py b/multiversx_sdk_cli/projects/report/data/folder_report.py index 31c78064..d0559379 100644 --- a/multiversx_sdk_cli/projects/report/data/folder_report.py +++ b/multiversx_sdk_cli/projects/report/data/folder_report.py @@ -1,9 +1,12 @@ 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 +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: @@ -17,6 +20,7 @@ def to_json(self) -> Any: '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) diff --git a/multiversx_sdk_cli/sign_verify.py b/multiversx_sdk_cli/sign_verify.py index 240a843e..09b45a55 100644 --- a/multiversx_sdk_cli/sign_verify.py +++ b/multiversx_sdk_cli/sign_verify.py @@ -1,7 +1,6 @@ from typing import Dict -from multiversx_sdk_core import Address, Message, MessageComputer -from multiversx_sdk_wallet import UserVerifier +from multiversx_sdk import Address, Message, MessageComputer, UserVerifier from multiversx_sdk_cli.accounts import Account diff --git a/multiversx_sdk_cli/tests/test_cli_shared.py b/multiversx_sdk_cli/tests/test_cli_shared.py index 04944866..4a1b1107 100644 --- a/multiversx_sdk_cli/tests/test_cli_shared.py +++ b/multiversx_sdk_cli/tests/test_cli_shared.py @@ -40,7 +40,7 @@ def test_args_obj_to_list(): assert args_list[1] == contract_build_args.path assert args_list[2] == "--no-wasm-opt" - contract_build_args.ignore = "random_directory" + 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) diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index 5255b4d3..3a8e0db6 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import Any -from multiversx_sdk_wallet import Mnemonic, UserPEM, UserWallet +from multiversx_sdk import Mnemonic, UserPEM, UserWallet from multiversx_sdk_cli.cli import main @@ -376,4 +376,4 @@ def _read_stdout(capsys: Any) -> str: def _mock_getpass(monkeypatch: Any, password: str): - monkeypatch.setattr(getpass, "getpass", lambda _: password) + monkeypatch.setattr(getpass, "getpass", lambda _: password) # type: ignore diff --git a/multiversx_sdk_cli/tests/test_contracts.py b/multiversx_sdk_cli/tests/test_contracts.py index 733969a3..de2a507b 100644 --- a/multiversx_sdk_cli/tests/test_contracts.py +++ b/multiversx_sdk_cli/tests/test_contracts.py @@ -3,7 +3,7 @@ import pytest from Cryptodome.Hash import keccak -from multiversx_sdk_core.address import Address +from multiversx_sdk import Address from multiversx_sdk_cli import errors from multiversx_sdk_cli.accounts import Account diff --git a/multiversx_sdk_cli/tests/test_proxy.py b/multiversx_sdk_cli/tests/test_proxy.py index 969aecd2..8cff09a5 100644 --- a/multiversx_sdk_cli/tests/test_proxy.py +++ b/multiversx_sdk_cli/tests/test_proxy.py @@ -1,6 +1,4 @@ -from multiversx_sdk_core import Address -from multiversx_sdk_network_providers.proxy_network_provider import \ - ProxyNetworkProvider +from multiversx_sdk import Address, ProxyNetworkProvider from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.cli import main diff --git a/multiversx_sdk_cli/tests/test_validators_core.py b/multiversx_sdk_cli/tests/test_validators_core.py index f3a7bcce..0066b345 100644 --- a/multiversx_sdk_cli/tests/test_validators_core.py +++ b/multiversx_sdk_cli/tests/test_validators_core.py @@ -1,6 +1,6 @@ from pathlib import Path -from multiversx_sdk_core import Address +from multiversx_sdk import Address from multiversx_sdk_cli.validators.core import \ prepare_transaction_data_for_stake diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 6f32c511..439c2a5d 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -4,7 +4,7 @@ import time from typing import Any, Dict, Optional, Protocol, TextIO -from multiversx_sdk_core import Address, Transaction, TransactionPayload +from multiversx_sdk import Address, Transaction, TransactionPayload from multiversx_sdk_cli import errors from multiversx_sdk_cli.accounts import Account, LedgerAccount diff --git a/multiversx_sdk_cli/utils.py b/multiversx_sdk_cli/utils.py index f2af91ef..a21596d4 100644 --- a/multiversx_sdk_cli/utils.py +++ b/multiversx_sdk_cli/utils.py @@ -11,10 +11,8 @@ from typing import (Any, Dict, List, Optional, Protocol, Union, runtime_checkable) -import requests_cache import toml -import multiversx_sdk_cli.config from multiversx_sdk_cli import errors logger = logging.getLogger("utils") @@ -149,17 +147,6 @@ def mark_executable(file: str) -> None: os.chmod(file, st.st_mode | stat.S_IEXEC) -def find_in_dictionary(dictionary, compound_path): - keys = compound_path.split(".") - node = dictionary - for key in keys: - node = node.get(key) - if node is None: - break - - return node - - 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)] @@ -210,7 +197,7 @@ def str_int_to_hex_str(number_str: str) -> str: return bytes_str -def parse_keys(bls_public_keys): +def parse_keys(bls_public_keys: str): keys = bls_public_keys.split(',') parsed_keys = '' for key in keys: diff --git a/multiversx_sdk_cli/validators/core.py b/multiversx_sdk_cli/validators/core.py index 61d0c136..0e6e9f95 100644 --- a/multiversx_sdk_cli/validators/core.py +++ b/multiversx_sdk_cli/validators/core.py @@ -2,9 +2,7 @@ from pathlib import Path from typing import Any, List, Tuple, Union -from multiversx_sdk_core import Address -from multiversx_sdk_wallet.validator_pem import ValidatorPEM -from multiversx_sdk_wallet.validator_signer import ValidatorSigner +from multiversx_sdk import Address, ValidatorPEM, ValidatorSigner from multiversx_sdk_cli import utils from multiversx_sdk_cli.accounts import Account diff --git a/multiversx_sdk_cli/validators/validators_file.py b/multiversx_sdk_cli/validators/validators_file.py index 4a664aac..ab10535a 100644 --- a/multiversx_sdk_cli/validators/validators_file.py +++ b/multiversx_sdk_cli/validators/validators_file.py @@ -2,9 +2,7 @@ from pathlib import Path from typing import Dict, List -from multiversx_sdk_wallet import ValidatorSigner -from multiversx_sdk_wallet.validator_keys import ValidatorPublicKey -from multiversx_sdk_wallet.validator_pem import ValidatorPEM +from multiversx_sdk import ValidatorPEM, ValidatorPublicKey, ValidatorSigner from multiversx_sdk_cli import guards from multiversx_sdk_cli.errors import CannotReadValidatorsData diff --git a/pyproject.toml b/pyproject.toml index da76bd45..ac423476 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,9 +27,7 @@ dependencies = [ "semver", "requests-cache", "rich==13.3.4", - "multiversx-sdk-network-providers>=0.13.0,<0.14.0", - "multiversx-sdk-wallet>=0.9.0,<0.10.0", - "multiversx-sdk-core>=0.8.0,<0.9.0", + "multiversx-sdk>=0.9.2,<1.0.0", "argcomplete==3.2.2" ] diff --git a/requirements.txt b/requirements.txt index 1d7aa8f9..c6f46432 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,4 @@ requests-cache rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk-core>=0.8.0,<0.9.0 -multiversx-sdk-network-providers>=0.13.0,<0.14.0 -multiversx-sdk-wallet>=0.9.0,<0.10.0 +multiversx-sdk>=0.9.2,<1.0.0 From 072f982645e7e4dd8c343794f79aba06bccfa8e4 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 11 Jun 2024 12:32:05 +0300 Subject: [PATCH 18/82] remove missleading flag --- multiversx_sdk_cli/cli_delegation.py | 1 - .../delegation/staking_provider.py | 18 +----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index b5601ed8..feecd36d 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -32,7 +32,6 @@ 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, help="a JSON file describing the Nodes") sub.add_argument("--delegation-contract", required=True, help="address of the delegation contract") - sub.add_argument("--using-delegation-manager", action="store_true", required=False, help="whether delegation contract was created using the Delegation Manager") _add_common_arguments(args, sub) sub.set_defaults(func=add_new_nodes) diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation/staking_provider.py index 5b9de82c..e8244ea0 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -4,8 +4,6 @@ from multiversx_sdk import (Address, DelegationTransactionsFactory, ValidatorPublicKey) -from multiversx_sdk_cli.accounts import Account, LedgerAccount -from multiversx_sdk_cli.cli_password import load_password from multiversx_sdk_cli.errors import BadUsage from multiversx_sdk_cli.interfaces import IAddress, ITransaction from multiversx_sdk_cli.validators.validators_file import ValidatorsFile @@ -331,7 +329,7 @@ def _get_public_keys_and_signed_messages(self, args: Any) -> Tuple[List[Validato validators_file = ValidatorsFile(validators_file_path) signers = validators_file.load_signers() - pubkey = self._get_pubkey_to_be_signed(args) + pubkey = Address.new_from_bech32(args.delegation_contract).get_public_key() public_keys: List[ValidatorPublicKey] = [] signed_messages: List[bytes] = [] @@ -342,17 +340,3 @@ def _get_public_keys_and_signed_messages(self, args: Any) -> Tuple[List[Validato signed_messages.append(signed_message) return public_keys, signed_messages - - def _get_pubkey_to_be_signed(self, args: Any) -> bytes: - account = Account() - if args.using_delegation_manager: - account = Account(address=Address.new_from_bech32(args.delegation_contract)) - elif args.pem: - account = Account(pem_file=args.pem) - 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) - - return account.address.get_public_key() From a3780b84277bee7c297e6ddfe3e057d57e39332e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 11 Jun 2024 13:06:55 +0300 Subject: [PATCH 19/82] fix unit tests --- multiversx_sdk_cli/tests/test_cli_staking_provider.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/multiversx_sdk_cli/tests/test_cli_staking_provider.py b/multiversx_sdk_cli/tests/test_cli_staking_provider.py index 7c141820..51cb7687 100644 --- a/multiversx_sdk_cli/tests/test_cli_staking_provider.py +++ b/multiversx_sdk_cli/tests/test_cli_staking_provider.py @@ -74,11 +74,11 @@ def test_add_nodes(capsys: Any): data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "addNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208@604882237a9845f508ad03877b5aab90569683eeb51fafcbbeb87440ba359992b3c0b837a8757c25be18132549404f88@78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d@ec54a009695af56c3585ef623387b67b6df1974b0b3c9138eb64bde6eb33978ae9851112b20c99bf63588e8e949e4388@7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491@c6c637de17db5f89a2fa1d1d935cb60c0e5e8958d3bfc47f903f774dd97398c8fe22093e113865ee98c3afdd1de62694" + assert data == "addNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208@307ef00b648eed52ce4e95f8155f2e65491addd49266254f7c32ff06622e335752a6a6ad2efeb2534ec0db6a431b4989@78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d@4c90003d4b535fe709b6583708ae276e29558c58d160ad6241c3063e590611a2e327f2b8299b4955179a85eab50b4587@7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491@1d6cf6b0a38fa5c10df6493eae0b14c5d119d9f7d3d3e790a047e4e7c09919f64f910971ceef380b49f70fc982d07d19" assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 20367000 - assert transaction["signature"] == "b383909206bf9631d5bef583c6e28250815494b459977fe8f037c2a97d2692a77d1b5c5dda6095d64ad180d213b5fd5eb7038a54af3765a3cb3fd86b86a1f305" + assert transaction["signature"] == "a0a8ce8ee6c60bd44b3940e47f3598fc95b47d111cf3ce7752951c692f98ccc8e0a2eade3ac55fcc68c54f98c69a0fb7aa0c93a50e8a8bcd0206f84a95e6390a" def test_add_nodes_with_gas_limit(capsys: Any): @@ -97,11 +97,11 @@ def test_add_nodes_with_gas_limit(capsys: Any): data = tx["emittedTransactionData"] transaction = tx["emittedTransaction"] - assert data == "addNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208@604882237a9845f508ad03877b5aab90569683eeb51fafcbbeb87440ba359992b3c0b837a8757c25be18132549404f88@78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d@ec54a009695af56c3585ef623387b67b6df1974b0b3c9138eb64bde6eb33978ae9851112b20c99bf63588e8e949e4388@7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491@c6c637de17db5f89a2fa1d1d935cb60c0e5e8958d3bfc47f903f774dd97398c8fe22093e113865ee98c3afdd1de62694" + assert data == "addNodes@e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208@307ef00b648eed52ce4e95f8155f2e65491addd49266254f7c32ff06622e335752a6a6ad2efeb2534ec0db6a431b4989@78689fd4b1e2e434d567fe01e61598a42717d83124308266bd09ccc15d2339dd318c019914b86ac29adbae5dd8a02d0307425e9bd85a296e94943708c72f8c670f0b7c50a890a5719088dbd9f1d062cad9acffa06df834106eebe1a4257ef00d@4c90003d4b535fe709b6583708ae276e29558c58d160ad6241c3063e590611a2e327f2b8299b4955179a85eab50b4587@7188b234a8bf834f2e6258012aa09a2ab93178ffab9c789480275f61fe02cd1b9a58ddc63b79a73abea9e2b7ac5cac0b0d4324eff50aca2f0ec946b9ae6797511fa3ce461b57e77129cba8ab3b51147695d4ce889cbe67905f6586b4e4f22491@1d6cf6b0a38fa5c10df6493eae0b14c5d119d9f7d3d3e790a047e4e7c09919f64f910971ceef380b49f70fc982d07d19" assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 20367001 - assert transaction["signature"] == "a889b50844eb5b33d410cbc8c8d2d88eebd64839d22fc1d246b82315123bd3acc6ad4445f9d2430965ce516ff89256faea42a97ebba2e1b386147ff8328b2e01" + assert transaction["signature"] == "b4aa91a3c865cf82784f29a659c0884cb70948e0e3f0b74ec8fe5a3cce358e750c357f1a1f9378e855a4261fff10abd927d1e3535fd18e99c40a562e31d67406" def test_remove_nodes_with_bls_keys(capsys: Any): From 8c2d0f55e6de6a34263333e421b51c4a98f425f7 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 12 Jun 2024 11:25:22 +0300 Subject: [PATCH 20/82] add command to create delegation contract from validator data --- multiversx_sdk_cli/cli_delegation.py | 23 +++++++++++++++ .../delegation/staking_provider.py | 28 ++++++++++++++++++- .../tests/test_cli_staking_provider.py | 19 +++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index feecd36d..255d1735 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -131,6 +131,15 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: _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=f"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) + def _add_common_arguments(args: List[str], sub: Any): cli_shared.add_proxy_arg(sub) @@ -334,3 +343,17 @@ def set_metadata(args: Any): tx = delegation.prepare_transaction_for_setting_metadata(sender, args) cli_shared.send_or_simulate(tx, args) + + +def make_new_contract_from_validator_data(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) + config = TransactionsFactoryConfig(args.chain) + delegation = DelegationOperations(config) + + tx = delegation.prepare_transaction_for_creating_delegation_contract_from_validator(sender, args) + 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 e8244ea0..beda7759 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -2,7 +2,8 @@ from typing import Any, List, Protocol, Tuple from multiversx_sdk import (Address, DelegationTransactionsFactory, - ValidatorPublicKey) + Transaction, ValidatorPublicKey) +from multiversx_sdk.core.serializer import args_to_string from multiversx_sdk_cli.errors import BadUsage from multiversx_sdk_cli.interfaces import IAddress, ITransaction @@ -307,6 +308,31 @@ 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) -> ITransaction: + receiver = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6" + max_cap = int(args.max_cap) + fee = int(args.fee) + data = "makeNewContractFromValidatorData@" + args_to_string([max_cap, fee]) + + tx = Transaction( + sender=owner.address.to_bech32(), + receiver=receiver, + 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 + ) + + if args.gas_limit: + tx.gas_limit = int(args.gas_limit) + + tx.signature = bytes.fromhex(owner.sign_transaction(tx)) + + 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) diff --git a/multiversx_sdk_cli/tests/test_cli_staking_provider.py b/multiversx_sdk_cli/tests/test_cli_staking_provider.py index 51cb7687..c1eb03a1 100644 --- a/multiversx_sdk_cli/tests/test_cli_staking_provider.py +++ b/multiversx_sdk_cli/tests/test_cli_staking_provider.py @@ -374,6 +374,25 @@ def test_set_metadata(capsys: Any): assert transaction["gasLimit"] == 11131000 +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" + ]) + tx = get_transaction(capsys) + data = tx["emittedTransactionData"] + transaction = tx["emittedTransaction"] + + assert data == "makeNewContractFromValidatorData@@0ea1" + assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6" + assert transaction["gasLimit"] == 510000000 + + def _read_stdout(capsys: Any) -> str: return capsys.readouterr().out.strip() From cda15091f8e6dc27d976566cf59ffda5b75029e1 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 12 Jun 2024 13:20:40 +0300 Subject: [PATCH 21/82] add commands for managing delegated funds --- multiversx_sdk_cli/cli_delegation.py | 114 ++++++++++++++++++ .../delegation/staking_provider.py | 97 +++++++++++++++ .../tests/test_cli_staking_provider.py | 92 ++++++++++++++ 3 files changed, 303 insertions(+) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 255d1735..44328332 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -80,6 +80,41 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: _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") + _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") + _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") + _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") + _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") + _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") @@ -140,6 +175,9 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: _add_common_arguments(args, sub) sub.set_defaults(func=make_new_contract_from_validator_data) + parser.epilog = cli_shared.build_group_epilog(subparsers) + return subparsers + def _add_common_arguments(args: List[str], sub: Any): cli_shared.add_proxy_arg(sub) @@ -275,6 +313,82 @@ def unjail_nodes(args: Any): cli_shared.send_or_simulate(tx, args) +def delegate(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) + + if not (int(args.value)): + raise errors.BadUrlError("Value not provided. Minimum value to delegate is 1 EGLD") + + sender = cli_shared.prepare_account(args) + config = TransactionsFactoryConfig(args.chain) + delegation = DelegationOperations(config) + + tx = delegation.prepare_transaction_for_delegating(sender, args) + cli_shared.send_or_simulate(tx, args) + + +def claim_rewards(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) + config = TransactionsFactoryConfig(args.chain) + delegation = DelegationOperations(config) + + tx = delegation.prepare_transaction_for_claiming_rewards(sender, args) + cli_shared.send_or_simulate(tx, args) + + +def redelegate_rewards(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) + config = TransactionsFactoryConfig(args.chain) + delegation = DelegationOperations(config) + + tx = delegation.prepare_transaction_for_redelegating_rewards(sender, args) + cli_shared.send_or_simulate(tx, args) + + +def undelegate(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) + + if not (int(args.value)): + raise errors.BadUrlError("Value not provided. Minimum value to undelegate is 1 EGLD") + + sender = cli_shared.prepare_account(args) + config = TransactionsFactoryConfig(args.chain) + delegation = DelegationOperations(config) + + tx = delegation.prepare_transaction_for_undelegating(sender, args) + cli_shared.send_or_simulate(tx, args) + + +def withdraw(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) + config = TransactionsFactoryConfig(args.chain) + delegation = DelegationOperations(config) + + tx = delegation.prepare_transaction_for_withdrawing(sender, args) + cli_shared.send_or_simulate(tx, args) + + def change_service_fee(args: Any): cli_shared.check_guardian_and_options_args(args) cli_shared.check_broadcast_args(args) diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation/staking_provider.py index beda7759..4d4834bb 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -190,6 +190,103 @@ def prepare_transaction_for_unjailing_nodes(self, owner: IAccount, args: Any) -> return tx + def prepare_transaction_for_delegating(self, owner: IAccount, args: Any) -> ITransaction: + delegation_contract = Address.new_from_bech32(args.delegation_contract) + + tx = self._factory.create_transaction_for_delegating( + sender=owner.address, + delegation_contract=delegation_contract, + amount=int(args.value) + ) + 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 = bytes.fromhex(owner.sign_transaction(tx)) + + return tx + + def prepare_transaction_for_claiming_rewards(self, owner: IAccount, args: Any) -> ITransaction: + delegation_contract = Address.new_from_bech32(args.delegation_contract) + + 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 = bytes.fromhex(owner.sign_transaction(tx)) + + return tx + + def prepare_transaction_for_redelegating_rewards(self, owner: IAccount, args: Any) -> ITransaction: + delegation_contract = Address.new_from_bech32(args.delegation_contract) + + 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 = bytes.fromhex(owner.sign_transaction(tx)) + + return tx + + def prepare_transaction_for_undelegating(self, owner: IAccount, args: Any) -> ITransaction: + delegation_contract = Address.new_from_bech32(args.delegation_contract) + + tx = self._factory.create_transaction_for_undelegating( + sender=owner.address, + delegation_contract=delegation_contract, + amount=int(args.value) + ) + 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 = bytes.fromhex(owner.sign_transaction(tx)) + + return tx + + def prepare_transaction_for_withdrawing(self, owner: IAccount, args: Any) -> ITransaction: + delegation_contract = Address.new_from_bech32(args.delegation_contract) + + 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 = bytes.fromhex(owner.sign_transaction(tx)) + + return tx + def prepare_transaction_for_changing_service_fee(self, owner: IAccount, args: Any) -> ITransaction: delegation_contract = Address.new_from_bech32(args.delegation_contract) diff --git a/multiversx_sdk_cli/tests/test_cli_staking_provider.py b/multiversx_sdk_cli/tests/test_cli_staking_provider.py index c1eb03a1..6921fe4b 100644 --- a/multiversx_sdk_cli/tests/test_cli_staking_provider.py +++ b/multiversx_sdk_cli/tests/test_cli_staking_provider.py @@ -393,6 +393,98 @@ def test_create_delegation_contract_from_validator(capsys: Any): assert transaction["gasLimit"] == 510000000 +def test_delegate(capsys: Any): + 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"] + + assert data == "delegate" + assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" + assert transaction["gasLimit"] == 12000000 + + +def test_claim_rewards(capsys: Any): + 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"] + + assert data == "claimRewards" + assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" + assert transaction["gasLimit"] == 6000000 + + +def test_redelegate_rewards(capsys: Any): + 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"] + + assert data == "reDelegateRewards" + assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" + assert transaction["gasLimit"] == 12000000 + + +def test_undelegate(capsys: Any): + 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"] + + assert data == "unDelegate@0de0b6b3a7640000" + assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" + assert transaction["gasLimit"] == 12000000 + + +def test_withdraw(capsys: Any): + 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"] + + assert data == "withdraw" + assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" + assert transaction["gasLimit"] == 12000000 + + def _read_stdout(capsys: Any) -> str: return capsys.readouterr().out.strip() From 06599c0494cd10483e2da0ee5c61fd132ceb0c31 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 12 Jun 2024 15:17:49 +0300 Subject: [PATCH 22/82] extract arguments check in a separate method --- multiversx_sdk_cli/cli_delegation.py | 91 +++++++--------------------- 1 file changed, 22 insertions(+), 69 deletions(-) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 44328332..9c49d158 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -188,12 +188,16 @@ def _add_common_arguments(args: List[str], sub: Any): cli_shared.add_guardian_wallet_args(args, sub) -def do_create_delegation_contract(args: Any): +def ensure_arguments_are_provided_and_prepared(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) + +def do_create_delegation_contract(args: Any): + ensure_arguments_are_provided_and_prepared(args) + sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) delegation = DelegationOperations(config) @@ -217,10 +221,7 @@ def get_contract_address_by_deploy_tx_hash(args: Any): def add_new_nodes(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -232,10 +233,7 @@ def add_new_nodes(args: Any): def remove_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -247,10 +245,7 @@ def remove_nodes(args: Any): def stake_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -270,10 +265,7 @@ 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) - 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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -285,10 +277,7 @@ def unbond_nodes(args: Any): def unstake_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -300,10 +289,7 @@ def unstake_nodes(args: Any): def unjail_nodes(args: Any): _check_if_either_bls_keys_or_validators_file_are_provided(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -314,10 +300,7 @@ def unjail_nodes(args: Any): def delegate(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) + ensure_arguments_are_provided_and_prepared(args) if not (int(args.value)): raise errors.BadUrlError("Value not provided. Minimum value to delegate is 1 EGLD") @@ -331,10 +314,7 @@ def delegate(args: Any): def claim_rewards(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -345,10 +325,7 @@ def claim_rewards(args: Any): def redelegate_rewards(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -359,10 +336,7 @@ def redelegate_rewards(args: Any): def undelegate(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) + ensure_arguments_are_provided_and_prepared(args) if not (int(args.value)): raise errors.BadUrlError("Value not provided. Minimum value to undelegate is 1 EGLD") @@ -376,10 +350,7 @@ def undelegate(args: Any): def withdraw(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -390,10 +361,7 @@ def withdraw(args: Any): def change_service_fee(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -404,10 +372,7 @@ def change_service_fee(args: Any): def modify_delegation_cap(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -418,10 +383,7 @@ def modify_delegation_cap(args: Any): def automatic_activation(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -432,10 +394,7 @@ def automatic_activation(args: Any): def redelegate_cap(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -446,10 +405,7 @@ def redelegate_cap(args: Any): def set_metadata(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) @@ -460,10 +416,7 @@ def set_metadata(args: Any): def make_new_contract_from_validator_data(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) + ensure_arguments_are_provided_and_prepared(args) sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) From 030cf554861b888992b1a9bc56087751ada04804 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 1 Jul 2024 12:59:50 +0300 Subject: [PATCH 23/82] fix import statement --- multiversx_sdk_cli/cli_faucet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/cli_faucet.py b/multiversx_sdk_cli/cli_faucet.py index 252970af..f5d40a63 100644 --- a/multiversx_sdk_cli/cli_faucet.py +++ b/multiversx_sdk_cli/cli_faucet.py @@ -3,7 +3,7 @@ from enum import Enum from typing import Any, List, Tuple -from multiversx_sdk_core import Message, MessageComputer +from multiversx_sdk import Message, MessageComputer from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.errors import BadUserInput From ec5747bffc327cb6c295f1f0134dc9be3aa38dd5 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 1 Jul 2024 15:25:56 +0300 Subject: [PATCH 24/82] add support for esdt transfers --- multiversx_sdk_cli/cli_transactions.py | 7 ++ .../tests/test_cli_transactions.py | 39 +++++++++ multiversx_sdk_cli/transactions.py | 82 ++++++++++++++----- 3 files changed, 107 insertions(+), 21 deletions(-) diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 5e02cac1..0956f328 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -17,6 +17,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: sub = cli_shared.add_command_subparser(subparsers, "tx", "new", f"Create a new transaction.{CLIOutputBuilder.describe()}") _add_common_arguments(args, sub) + _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) @@ -61,6 +62,12 @@ def _add_common_arguments(args: List[str], sub: Any): sub.add_argument("--data-file", type=str, default=None, help="a file containing transaction data") +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") + + def create_transaction(args: Any): args = utils.as_object(args) diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index 35897304..157b181b 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -48,5 +48,44 @@ def test_create_tx_and_sign_by_hash(capsys: Any): 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", + ]) + assert False if return_code else True + tx = _read_stdout(capsys) + tx_json = json.loads(tx) + signature = tx_json["emittedTransaction"]["signature"] + 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", + ]) + assert False if return_code else True + tx = _read_stdout(capsys) + tx_json = json.loads(tx) + signature = tx_json["emittedTransaction"]["signature"] + assert signature == "575b029d52ff5ffbfb7bab2f04052de88a6f7d022a6ad368459b8af9acaed3717d3f95db09f460649a8f405800838bc2c432496bd03c9039ea166bd32b84660e" + + 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 439c2a5d..96381258 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -2,9 +2,12 @@ import json import logging import time -from typing import Any, Dict, Optional, Protocol, TextIO +from typing import Any, Dict, List, Optional, Protocol, TextIO -from multiversx_sdk import Address, Transaction, TransactionPayload +from multiversx_sdk import (Address, Token, TokenComputer, TokenTransfer, + Transaction, TransactionPayload, + TransactionsFactoryConfig, + TransferTransactionsFactory) from multiversx_sdk_cli import errors from multiversx_sdk_cli.accounts import Account, LedgerAccount @@ -55,6 +58,48 @@ def __init__(self) -> None: def do_prepare_transaction(args: Any) -> Transaction: + account = load_sender_account_from_args(args) + transfers = getattr(args, "token_transfers", None) + transfers = prepare_token_transfers(transfers) if transfers else None + + config = TransactionsFactoryConfig(args.chain) + factory = TransferTransactionsFactory(config) + receiver = Address.new_from_bech32(args.receiver) + + # will be replaced with 'create_transaction_for_transfer' + if transfers: + tx = factory.create_transaction_for_esdt_token_transfer( + sender=account.address, + receiver=receiver, + token_transfers=transfers + ) + else: + tx = factory.create_transaction_for_native_token_transfer( + sender=account.address, + receiver=receiver, + native_amount=int(args.value), + data=str(args.data) + ) + + 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) + + if args.guardian: + tx.guardian = args.guardian + + tx.signature = bytes.fromhex(account.sign_transaction(tx)) + tx = sign_tx_by_guardian(args, tx) + + 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) @@ -64,28 +109,23 @@ def do_prepare_transaction(args: Any) -> Transaction: password = load_password(args) account = Account(key_file=args.keyfile, password=password) - tx = Transaction( - chain_id=args.chain, - sender=account.address.to_bech32(), - receiver=args.receiver, - gas_limit=int(args.gas_limit), - sender_username=getattr(args, "sender_username", ""), - receiver_username=getattr(args, "receiver_username", ""), - gas_price=int(args.gas_price), - data=str(args.data).encode(), - nonce=int(args.nonce), - value=int(args.value), - version=int(args.version), - options=int(args.options) - ) + return account - if args.guardian: - tx.guardian = args.guardian - tx.signature = bytes.fromhex(account.sign_transaction(tx)) - tx = sign_tx_by_guardian(args, tx) +def prepare_token_transfers(transfers: List[Any]): + token_computer = TokenComputer() + token_transfers: List[TokenTransfer] = [] - return tx + 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) -> Transaction: From 25c1f32e553c19960ad1746a8568a34e7458a35e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 2 Jul 2024 12:11:04 +0300 Subject: [PATCH 25/82] use extras api --- multiversx_sdk_cli/cli_faucet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk_cli/cli_faucet.py b/multiversx_sdk_cli/cli_faucet.py index f5d40a63..df212a0e 100644 --- a/multiversx_sdk_cli/cli_faucet.py +++ b/multiversx_sdk_cli/cli_faucet.py @@ -19,8 +19,8 @@ class WebWalletUrls(Enum): class ApiUrls(Enum): - DEVNET = "https://devnet-api.multiversx.com" - TESTNET = "https://testnet-api.multiversx.com" + DEVNET = "https://devnet-extras-api.multiversx.com" + TESTNET = "https://testnet-extras-api.multiversx.com" def setup_parser(args: List[str], subparsers: Any) -> Any: From 6c0450a2d8284d2ae6624deced036f3889e318af Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 2 Jul 2024 12:38:07 +0300 Subject: [PATCH 26/82] revert latest changes --- multiversx_sdk_cli/cli_faucet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk_cli/cli_faucet.py b/multiversx_sdk_cli/cli_faucet.py index df212a0e..f5d40a63 100644 --- a/multiversx_sdk_cli/cli_faucet.py +++ b/multiversx_sdk_cli/cli_faucet.py @@ -19,8 +19,8 @@ class WebWalletUrls(Enum): class ApiUrls(Enum): - DEVNET = "https://devnet-extras-api.multiversx.com" - TESTNET = "https://testnet-extras-api.multiversx.com" + DEVNET = "https://devnet-api.multiversx.com" + TESTNET = "https://testnet-api.multiversx.com" def setup_parser(args: List[str], subparsers: Any) -> Any: From 148e63ecfe8e0ae1f6421bd43354dccbdb62514b Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 3 Jul 2024 10:14:16 +0300 Subject: [PATCH 27/82] add abi argument for contract operations --- multiversx_sdk_cli/cli_contracts.py | 21 ++++++++++++++++++++- multiversx_sdk_cli/contracts.py | 5 +++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 62cc983b..f6a67cde 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -1,3 +1,4 @@ +import json import logging import os from pathlib import Path @@ -5,6 +6,7 @@ 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 @@ -89,6 +91,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: 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) cli_shared.add_wallet_args(args, sub) cli_shared.add_proxy_arg(sub) @@ -224,6 +227,10 @@ def _add_contract_arg(sub: Any): sub.add_argument("contract", help="🖄 the address of the Smart Contract") +def _add_contract_abi_arg(sub: Any): + sub.add_argument("--abi", help="the ABI of the Smart Contract") + + def _add_function_arg(sub: Any): sub.add_argument("--function", required=True, type=str, help="the function to call") @@ -232,6 +239,8 @@ 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", help="a json file containing the arguments. ONLY if abi file is provided. " + "E.g. { 'to': 'erd1...', 'amount': 10000000000 }") def _add_token_transfers_args(sub: Any): @@ -363,10 +372,20 @@ def call(args: Any): cli_shared.prepare_nonce_in_args(args) sender = cli_shared.prepare_account(args) + abi = Abi.load(Path(args.abi)) if args.abi else None + config = TransactionsFactoryConfig(args.chain) - contract = SmartContract(config) + contract = SmartContract(config, abi) + contract_address = Address.new_from_bech32(args.contract) + json_args = json.loads(Path(args.arguments_json).expanduser().read_text()) if args.arguments_json else None + + if json_args and args.arguments: + raise Exception("Both '--arguments' and '--arguments-json' provided.") + + # check what kind of args were provided and pass them further. + tx = contract.prepare_execute_transaction( caller=sender, contract=contract_address, diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 515dec52..6bf3b7a0 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -6,6 +6,7 @@ from multiversx_sdk import (Address, SmartContractTransactionsFactory, Token, TokenComputer, TokenTransfer, Transaction, TransactionPayload) +from multiversx_sdk.abi import Abi from multiversx_sdk.network_providers.interface import IContractQuery from multiversx_sdk_cli import errors @@ -73,8 +74,8 @@ class IConfig(Protocol): class SmartContract: - def __init__(self, config: IConfig): - self._factory = SmartContractTransactionsFactory(config) + def __init__(self, config: IConfig, abi: Optional[Abi] = None): + self._factory = SmartContractTransactionsFactory(config, abi) def prepare_deploy_transaction(self, owner: Account, From 98d88581b152ebe02dfcdbc6e1855e605cf21a11 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 3 Jul 2024 11:40:39 +0300 Subject: [PATCH 28/82] reference sdk-py from feat branch --- multiversx_sdk_cli/cli_shared.py | 14 +++++++++++++- multiversx_sdk_cli/cli_transactions.py | 4 ++++ multiversx_sdk_cli/interfaces.py | 7 ++++++- requirements.txt | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 10ba00fe..002659a4 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -63,7 +63,15 @@ def add_command_subparser(subparsers: Any, group: str, command: str, description ) -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_guardian: bool = False): +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_guardian: bool = False, + with_relayed_v3: 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)") @@ -88,6 +96,10 @@ def add_tx_args(args: List[str], sub: Any, with_nonce: bool = True, with_receive if with_guardian: add_guardian_args(sub) + if with_relayed_v3: + sub.add_argument("--relayer", help="the address of the relayer") + sub.add_argument("--inner-transactions", help="a json file containing the inner transactions; should only be provided when creating the relayer's transaction") + sub.add_argument("--options", type=int, default=0, help="the transaction options (default: 0)") diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 5e02cac1..999d2c19 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -1,3 +1,4 @@ +import logging from pathlib import Path from typing import Any, List @@ -10,6 +11,8 @@ do_prepare_transaction, load_transaction_from_file) +logger = logging.getLogger("cli.transactions") + def setup_parser(args: List[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser(subparsers, "tx", "Create and broadcast Transactions") @@ -75,6 +78,7 @@ 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 diff --git a/multiversx_sdk_cli/interfaces.py b/multiversx_sdk_cli/interfaces.py index e59c3c8f..e0db9a2f 100644 --- a/multiversx_sdk_cli/interfaces.py +++ b/multiversx_sdk_cli/interfaces.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Protocol +from typing import Any, Dict, Protocol, Sequence class IAddress(Protocol): @@ -25,6 +25,11 @@ class ITransaction(Protocol): guardian: str signature: bytes guardian_signature: bytes + relayer: str + + @property + def inner_transactions(self) -> Sequence["ITransaction"]: + ... class IAccount(Protocol): diff --git a/requirements.txt b/requirements.txt index c6f46432..cadf372e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,4 @@ requests-cache rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk>=0.9.2,<1.0.0 +multiversx-sdk @ git+https://github.com/multiversx/mx-sdk-py@feat/next From c0c817bbf96d6687ed3469f756c298a93ac4c5c6 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 3 Jul 2024 17:17:36 +0300 Subject: [PATCH 29/82] add support for relayed v3 transactions --- multiversx_sdk_cli/cli_shared.py | 5 + multiversx_sdk_cli/cli_transactions.py | 37 +++++++- .../tests/test_cli_transactions.py | 78 +++++++++++++++- multiversx_sdk_cli/transactions.py | 91 +++++++------------ 4 files changed, 148 insertions(+), 63 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 002659a4..9b9ff606 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -99,6 +99,7 @@ def add_tx_args( if with_relayed_v3: sub.add_argument("--relayer", help="the address of the relayer") sub.add_argument("--inner-transactions", help="a json file containing the inner transactions; should only be provided when creating the relayer's transaction") + add_inner_transaction_outfile_arg(sub) sub.add_argument("--options", type=int, default=0, help="the transaction options (default: 0)") @@ -139,6 +140,10 @@ def add_outfile_arg(sub: Any, what: str = ""): sub.add_argument("--outfile", type=FileType("w"), default=sys.stdout, help=f"where to save the output {what} (default: stdout)") +def add_inner_transaction_outfile_arg(sub: Any): + sub.add_argument("--inner-transactions-outfile", type=str, help="where to save the transaction as an inner transaction (default: stdout)") + + def add_infile_arg(sub: Any, what: str = ""): what = f"({what})" if what else "" sub.add_argument("--infile", type=FileType("r"), required=True, help=f"input file {what}") diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 222e47dc..9484fcf4 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -1,14 +1,17 @@ import logging from pathlib import Path -from typing import Any, List +from typing import Any, Dict, List + +from multiversx_sdk import Transaction, TransactionsConverter from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.custom_network_provider import CustomNetworkProvider -from multiversx_sdk_cli.errors import NoWalletProvided +from multiversx_sdk_cli.errors import BadUsage, NoWalletProvided from multiversx_sdk_cli.transactions import (compute_relayed_v1_data, do_prepare_transaction, + load_inner_transactions_from_file, load_transaction_from_file) logger = logging.getLogger("cli.transactions") @@ -82,8 +85,14 @@ def create_transaction(args: Any): if args.data_file: args.data = Path(args.data_file).read_text() + check_relayer_transaction_with_data_field_for_relayed_v3(args) + tx = do_prepare_transaction(args) + if hasattr(args, "inner_transactions_outfile") and args.inner_transactions_outfile: + save_transaction_to_inner_transactions_file(tx, args) + return + 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)) @@ -92,6 +101,30 @@ def create_transaction(args: Any): cli_shared.send_or_simulate(tx, args) +def save_transaction_to_inner_transactions_file(transaction: Transaction, args: Any): + inner_txs_file = Path(args.inner_transactions_outfile).expanduser() + transactions = get_inner_transactions_if_any(inner_txs_file) + transactions.append(transaction) + + tx_converter = TransactionsConverter() + inner_transactions: Dict[str, Any] = {} + inner_transactions["innerTransactions"] = [tx_converter.transaction_to_dictionary(tx) for tx in transactions] + + with open(inner_txs_file, "w") as file: + utils.dump_out_json(inner_transactions, file) + + +def get_inner_transactions_if_any(file: Path) -> List[Transaction]: + if file.is_file(): + return load_inner_transactions_from_file(file) + return [] + + +def check_relayer_transaction_with_data_field_for_relayed_v3(args: Any): + if hasattr(args, "inner_transactions") and args.inner_transactions and args.data: + raise BadUsage("Can't set data field when creating a relayedV3 transaction") + + def send_transaction(args: Any): args = utils.as_object(args) diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index 157b181b..252d8146 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -1,10 +1,12 @@ import json +import os from pathlib import Path -from typing import Any +from typing import Any, List from multiversx_sdk_cli.cli import main testdata_path = Path(__file__).parent / "testdata" +testdata_out = Path(__file__).parent / "testdata-out" def test_relayed_v1_transaction(capsys: Any): @@ -87,5 +89,79 @@ def test_create_multi_transfer_transaction(capsys: Any): assert signature == "575b029d52ff5ffbfb7bab2f04052de88a6f7d022a6ad368459b8af9acaed3717d3f95db09f460649a8f405800838bc2c432496bd03c9039ea166bd32b84660e" +def test_create_and_save_inner_transaction(): + return_code = main([ + "tx", "new", + "--pem", str(testdata_path / "alice.pem"), + "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", "77", + "--gas-limit", "500000", + "--relayer", "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8", + "--inner-transactions-outfile", str(testdata_out / "inner_transactions.json"), + "--chain", "T", + ]) + assert False if return_code else True + assert Path(testdata_out / "inner_transactions.json").is_file() + + +def test_create_and_append_inner_transaction(): + return_code = main([ + "tx", "new", + "--pem", str(testdata_path / "alice.pem"), + "--receiver", "erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan", + "--nonce", "1234", + "--gas-limit", "50000", + "--relayer", "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8", + "--inner-transactions-outfile", str(testdata_out / "inner_transactions.json"), + "--chain", "T", + ]) + assert False if return_code else True + + with open(testdata_out / "inner_transactions.json", "r") as file: + json_file = json.load(file) + + inner_txs: List[Any] = json_file["innerTransactions"] + assert len(inner_txs) == 2 + + +def test_create_relayer_transaction(capsys: Any): + return_code = main([ + "tx", "new", + "--pem", str(testdata_path / "testUser.pem"), + "--receiver", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--nonce", "987", + "--gas-limit", "5000000", + "--inner-transactions", str(testdata_out / "inner_transactions.json"), + "--chain", "T", + ]) + # remove test file to ensure consistency when running test file locally + os.remove(testdata_out / "inner_transactions.json") + + assert False if return_code else True + + tx = _read_stdout(capsys) + tx_json = json.loads(tx)["emittedTransaction"] + + assert tx_json["sender"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert tx_json["receiver"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert tx_json["gasLimit"] == 5000000 + assert tx_json["nonce"] == 987 + assert tx_json["chainID"] == "T" + + # should be the two inner transactions created in the tests above + inner_transactions = tx_json["innerTransactions"] + assert len(inner_transactions) == 2 + + assert inner_transactions[0]["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert inner_transactions[0]["receiver"] == "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" + assert inner_transactions[0]["nonce"] == 77 + assert inner_transactions[0]["relayer"] == "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8" + + assert inner_transactions[1]["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert inner_transactions[1]["receiver"] == "erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan" + assert inner_transactions[1]["nonce"] == 1234 + assert inner_transactions[1]["relayer"] == "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8" + + 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 96381258..91b8386c 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -2,10 +2,11 @@ import json import logging import time +from pathlib import Path from typing import Any, Dict, List, Optional, Protocol, TextIO from multiversx_sdk import (Address, Token, TokenComputer, TokenTransfer, - Transaction, TransactionPayload, + Transaction, TransactionsConverter, TransactionsFactoryConfig, TransferTransactionsFactory) @@ -37,28 +38,10 @@ def get_transaction(self, tx_hash: str, with_process_status: Optional[bool] = Fa ... -class JSONTransaction: - def __init__(self) -> None: - self.hash = "" - self.nonce = 0 - self.value = "0" - self.receiver = "" - self.sender = "" - self.senderUsername = "" - self.receiverUsername = "" - self.gasPrice = 0 - self.gasLimit = 0 - self.data: str = "" - self.chainID = "" - self.version = 0 - self.options = 0 - self.signature = "" - self.guardian = "" - self.guardianSignature = "" - - def do_prepare_transaction(args: Any) -> Transaction: account = load_sender_account_from_args(args) + + native_amount = int(args.value) transfers = getattr(args, "token_transfers", None) transfers = prepare_token_transfers(transfers) if transfers else None @@ -66,19 +49,23 @@ def do_prepare_transaction(args: Any) -> Transaction: factory = TransferTransactionsFactory(config) receiver = Address.new_from_bech32(args.receiver) - # will be replaced with 'create_transaction_for_transfer' - if transfers: - tx = factory.create_transaction_for_esdt_token_transfer( + # temporary workaround until proper fix in sdk-py + if native_amount or transfers: + tx = factory.create_transaction_for_transfer( sender=account.address, receiver=receiver, - token_transfers=transfers + native_amount=native_amount, + token_transfers=transfers, + data=str(args.data).encode() ) else: - tx = factory.create_transaction_for_native_token_transfer( - sender=account.address, - receiver=receiver, - native_amount=int(args.value), - data=str(args.data) + tx = Transaction( + sender=account.address.to_bech32(), + receiver=receiver.to_bech32(), + value=native_amount, + data=str(args.data).encode(), + gas_limit=int(args.gas_limit), + chain_id=args.chain ) tx.gas_limit = int(args.gas_limit) @@ -93,6 +80,12 @@ def do_prepare_transaction(args: Any) -> Transaction: if args.guardian: tx.guardian = args.guardian + if args.relayer: + tx.relayer = Address.new_from_bech32(args.relayer).to_bech32() + + if args.inner_transactions: + tx.inner_transactions = load_inner_transactions_from_file(Path(args.inner_transactions).expanduser()) + tx.signature = bytes.fromhex(account.sign_transaction(tx)) tx = sign_tx_by_guardian(args, tx) @@ -231,37 +224,15 @@ def compute_relayed_v1_data(tx: Transaction) -> str: def load_transaction_from_file(f: TextIO) -> Transaction: data_json: bytes = f.read().encode() - fields = json.loads(data_json).get("tx") or json.loads(data_json).get("emittedTransaction") - - instance = JSONTransaction() - instance.__dict__.update(fields) - - loaded_tx = Transaction( - chain_id=instance.chainID, - sender=instance.sender, - receiver=instance.receiver, - sender_username=decode_field_value(instance.senderUsername), - receiver_username=decode_field_value(instance.receiverUsername), - gas_limit=instance.gasLimit, - gas_price=instance.gasPrice, - value=int(instance.value), - data=TransactionPayload.from_encoded_str(instance.data).data, - version=instance.version, - options=instance.options, - nonce=instance.nonce - ) - - if instance.guardian: - loaded_tx.guardian = instance.guardian - - if instance.signature: - loaded_tx.signature = bytes.fromhex(instance.signature) + transaction_dictionary = json.loads(data_json).get("tx") or json.loads(data_json).get("emittedTransaction") - if instance.guardianSignature: - loaded_tx.guardian_signature = bytes.fromhex(instance.guardianSignature) + tx_converter = TransactionsConverter() + return tx_converter.dictionary_to_transaction(transaction_dictionary) - return loaded_tx +def load_inner_transactions_from_file(path: Path) -> List[Transaction]: + data_json = path.read_bytes() + transactions: List[Dict[str, Any]] = json.loads(data_json).get("innerTransactions") -def decode_field_value(value: str) -> str: - return base64.b64decode(value).decode() + tx_converter = TransactionsConverter() + return [tx_converter.dictionary_to_transaction(transaction) for transaction in transactions] From 1cee88f641cdf8340a176c0f0d4dd5f47ed78fb3 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 3 Jul 2024 17:20:14 +0300 Subject: [PATCH 30/82] remove separate function for adding cli argument --- multiversx_sdk_cli/cli_shared.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 9b9ff606..6a155677 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -99,7 +99,7 @@ def add_tx_args( if with_relayed_v3: sub.add_argument("--relayer", help="the address of the relayer") sub.add_argument("--inner-transactions", help="a json file containing the inner transactions; should only be provided when creating the relayer's transaction") - add_inner_transaction_outfile_arg(sub) + sub.add_argument("--inner-transactions-outfile", type=str, help="where to save the transaction as an inner transaction (default: stdout)") sub.add_argument("--options", type=int, default=0, help="the transaction options (default: 0)") @@ -140,10 +140,6 @@ def add_outfile_arg(sub: Any, what: str = ""): sub.add_argument("--outfile", type=FileType("w"), default=sys.stdout, help=f"where to save the output {what} (default: stdout)") -def add_inner_transaction_outfile_arg(sub: Any): - sub.add_argument("--inner-transactions-outfile", type=str, help="where to save the transaction as an inner transaction (default: stdout)") - - def add_infile_arg(sub: Any, what: str = ""): what = f"({what})" if what else "" sub.add_argument("--infile", type=FileType("r"), required=True, help=f"input file {what}") From 3686e193f79297a7668780ea6916e05f8f0ed67c Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 11:15:54 +0300 Subject: [PATCH 31/82] add unit test for invalid relayed transaction --- multiversx_sdk_cli/tests/test_cli_transactions.py | 14 ++++++++++++++ multiversx_sdk_cli/transactions.py | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index 252d8146..c2787ad7 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -124,6 +124,20 @@ def test_create_and_append_inner_transaction(): assert len(inner_txs) == 2 +def test_create_invalid_relayed_transaction(): + return_code = main([ + "tx", "new", + "--pem", str(testdata_path / "testUser.pem"), + "--receiver", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--nonce", "987", + "--gas-limit", "5000000", + "--inner-transactions", str(testdata_out / "inner_transactions.json"), + "--data", "test data", + "--chain", "T", + ]) + assert return_code + + def test_create_relayer_transaction(capsys: Any): return_code = main([ "tx", "new", diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 91b8386c..2f951401 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -59,10 +59,10 @@ def do_prepare_transaction(args: Any) -> Transaction: 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.to_bech32(), receiver=receiver.to_bech32(), - value=native_amount, data=str(args.data).encode(), gas_limit=int(args.gas_limit), chain_id=args.chain From 98027ab489b3e80b0a5487f189918780f8cea602 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 11:20:35 +0300 Subject: [PATCH 32/82] use empty list instead of None --- multiversx_sdk_cli/tests/test_cli_transactions.py | 8 ++++---- multiversx_sdk_cli/transactions.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index 157b181b..1d1b4220 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -23,7 +23,7 @@ def test_relayed_v1_transaction(capsys: Any): "--chain", "T", "--relay" ]) - assert False if return_code else True + assert return_code == 0 relayed_tx = _read_stdout(capsys) assert relayed_tx == "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414141415141414141414141414141414141414141414141414141414141432f2f383d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225a3256305132397564484a68593352446232356d6157633d222c227369676e6174757265223a2239682b6e6742584f5536776674315464437368534d4b3454446a5a32794f74686336564c576e3478724d5a706248427738677a6c6659596d362b766b505258303764634a562b4745635462616a7049692b5a5a5942773d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a317d" @@ -40,7 +40,7 @@ def test_create_tx_and_sign_by_hash(capsys: Any): "--options", "1", "--chain", "integration tests chain ID", ]) - assert False if return_code else True + assert return_code == 0 tx = _read_stdout(capsys) tx_json = json.loads(tx) @@ -61,7 +61,7 @@ def test_create_move_balance_transaction(capsys: Any): "--options", "0", "--chain", "T", ]) - assert False if return_code else True + assert return_code == 0 tx = _read_stdout(capsys) tx_json = json.loads(tx) signature = tx_json["emittedTransaction"]["signature"] @@ -80,7 +80,7 @@ def test_create_multi_transfer_transaction(capsys: Any): "--options", "0", "--chain", "T", ]) - assert False if return_code else True + assert return_code == 0 tx = _read_stdout(capsys) tx_json = json.loads(tx) signature = tx_json["emittedTransaction"]["signature"] diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 96381258..65f53cc8 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -59,8 +59,8 @@ def __init__(self) -> None: def do_prepare_transaction(args: Any) -> Transaction: account = load_sender_account_from_args(args) - transfers = getattr(args, "token_transfers", None) - transfers = prepare_token_transfers(transfers) if transfers else None + transfers = getattr(args, "token_transfers", []) + transfers = prepare_token_transfers(transfers) if transfers else [] config = TransactionsFactoryConfig(args.chain) factory = TransferTransactionsFactory(config) @@ -112,7 +112,7 @@ def load_sender_account_from_args(args: Any) -> Account: return account -def prepare_token_transfers(transfers: List[Any]): +def prepare_token_transfers(transfers: List[Any]) -> List[TokenTransfer]: token_computer = TokenComputer() token_transfers: List[TokenTransfer] = [] From 28b172f783a38bf39e2feafd1d0e47e9b26f2eda Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 11:56:25 +0300 Subject: [PATCH 33/82] remove redundant if else statement --- 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 8cd1f9c9..9ffcc048 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -43,7 +43,7 @@ def do_prepare_transaction(args: Any) -> Transaction: native_amount = int(args.value) transfers = getattr(args, "token_transfers", []) - transfers = prepare_token_transfers(transfers) if transfers else [] + transfers = prepare_token_transfers(transfers) config = TransactionsFactoryConfig(args.chain) factory = TransferTransactionsFactory(config) From d6ec09735cc11f7133e0da8953149d650fdf7082 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 15:28:18 +0300 Subject: [PATCH 34/82] load args from file --- multiversx_sdk_cli/cli_contracts.py | 15 +++-- multiversx_sdk_cli/contracts.py | 73 ++++++++++++++-------- multiversx_sdk_cli/tests/test_contracts.py | 11 ++-- 3 files changed, 59 insertions(+), 40 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index f6a67cde..94f017c7 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -228,7 +228,7 @@ def _add_contract_arg(sub: Any): def _add_contract_abi_arg(sub: Any): - sub.add_argument("--abi", help="the ABI of the Smart Contract") + sub.add_argument("--abi", type=str, help="the ABI of the Smart Contract") def _add_function_arg(sub: Any): @@ -239,7 +239,7 @@ 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", help="a json file containing the arguments. ONLY if abi file is provided. " + 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 }") @@ -372,25 +372,24 @@ def call(args: Any): cli_shared.prepare_nonce_in_args(args) sender = cli_shared.prepare_account(args) - abi = Abi.load(Path(args.abi)) if args.abi else None config = TransactionsFactoryConfig(args.chain) + abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) - contract_address = Address.new_from_bech32(args.contract) - - json_args = json.loads(Path(args.arguments_json).expanduser().read_text()) if args.arguments_json else None + json_args = json.loads(Path(args.arguments_file).expanduser().read_text()) if args.arguments_file else None if json_args and args.arguments: raise Exception("Both '--arguments' and '--arguments-json' provided.") - # check what kind of args were provided and pass them further. + arguments = json_args or args.arguments + contract_address = Address.new_from_bech32(args.contract) tx = contract.prepare_execute_transaction( caller=sender, contract=contract_address, function=args.function, - arguments=args.arguments, + arguments=arguments, gas_limit=int(args.gas_limit), value=int(args.value), transfers=args.token_transfers, diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 6bf3b7a0..754130fd 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -1,7 +1,7 @@ import base64 import logging from pathlib import Path -from typing import Any, List, Optional, Protocol, Sequence, Union +from typing import Any, Dict, List, Optional, Protocol, Sequence, Union, cast from multiversx_sdk import (Address, SmartContractTransactionsFactory, Token, TokenComputer, TokenTransfer, Transaction, @@ -75,6 +75,7 @@ class IConfig(Protocol): class SmartContract: def __init__(self, config: IConfig, abi: Optional[Abi] = None): + self._abi = abi self._factory = SmartContractTransactionsFactory(config, abi) def prepare_deploy_transaction(self, @@ -91,7 +92,7 @@ def prepare_deploy_transaction(self, version: int, options: int, guardian: str) -> Transaction: - args = prepare_args_for_factory(arguments) if arguments else [] + args = self.prepare_args_for_factory("constructor", arguments) if arguments else [] tx = self._factory.create_transaction_for_deploy( sender=owner.address, @@ -116,7 +117,7 @@ def prepare_execute_transaction(self, caller: Account, contract: Address, function: str, - arguments: Union[List[str], None], + arguments: Any, gas_limit: int, value: int, transfers: Union[List[str], None], @@ -125,7 +126,7 @@ def prepare_execute_transaction(self, options: int, guardian: str) -> Transaction: token_transfers = self._prepare_token_transfers(transfers) if transfers else [] - args = prepare_args_for_factory(arguments) if arguments else [] + args = self.prepare_args_for_factory(function, arguments) if arguments else [] tx = self._factory.create_transaction_for_execute( sender=caller.address, @@ -159,7 +160,7 @@ def prepare_upgrade_transaction(self, version: int, options: int, guardian: str) -> Transaction: - args = prepare_args_for_factory(arguments) if arguments else [] + args = self.prepare_args_for_factory("upgrade_constructor", arguments) if arguments else [] tx = self._factory.create_transaction_for_upgrade( sender=owner.address, @@ -196,6 +197,46 @@ def _prepare_token_transfers(self, transfers: List[str]) -> List[TokenTransfer]: return token_transfers + def prepare_args_for_factory(self, endpoint: str, arguments: Any) -> List[Any]: + args: List[Any] = [] + + if isinstance(arguments, dict): + return self._prepare_args_using_abi(endpoint, arguments) + + if isinstance(arguments, list): + for arg in arguments: # type: ignore + arg = cast(str, arg) + if arg.startswith(HEX_PREFIX): + args.append(hex_to_bytes(arg)) + elif arg.isnumeric(): + args.append(int(arg)) + elif arg.startswith(DEFAULT_HRP): + args.append(Address.new_from_bech32(arg)) + elif arg.lower() == FALSE_STR_LOWER: + args.append(False) + elif arg.lower() == TRUE_STR_LOWER: + args.append(True) + elif arg.startswith(STR_PREFIX): + args.append(arg[len(STR_PREFIX):]) + else: + raise errors.BadUserInput(f"Unknown argument type for argument: `{arg}`. Use `mxpy contract --help` to check all supported arguments") + + return args + + def _prepare_args_using_abi(self, function: str, arguments: Dict[str, Any]): + if not self._abi: + raise Exception("Abi file not provided") + + endpoint_definition = [endpoint for endpoint in self._abi.definition.endpoints if endpoint.name == function] + if not endpoint_definition: + raise Exception(f"Endpoint [{function}] not found") + elif len(endpoint_definition) > 1: + raise Exception(f"More than one endpoint with name [{function}] found.") + else: + endpoint_definition = endpoint_definition[0] + + input_parameters_names = [input.name for input in endpoint_definition.inputs] + def query_contract( contract_address: IAddress, @@ -265,28 +306,6 @@ def prepare_execute_transaction_data(function: str, arguments: List[Any]) -> Tra return TransactionPayload.from_str(tx_data) -def prepare_args_for_factory(arguments: List[str]) -> List[Any]: - args: List[Any] = [] - - for arg in arguments: - if arg.startswith(HEX_PREFIX): - args.append(hex_to_bytes(arg)) - elif arg.isnumeric(): - args.append(int(arg)) - elif arg.startswith(DEFAULT_HRP): - args.append(Address.new_from_bech32(arg)) - elif arg.lower() == FALSE_STR_LOWER: - args.append(False) - elif arg.lower() == TRUE_STR_LOWER: - args.append(True) - elif arg.startswith(STR_PREFIX): - args.append(arg[len(STR_PREFIX):]) - else: - 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(arg: str): argument = arg[len(HEX_PREFIX):] argument = argument.upper() diff --git a/multiversx_sdk_cli/tests/test_contracts.py b/multiversx_sdk_cli/tests/test_contracts.py index de2a507b..d44bb3f9 100644 --- a/multiversx_sdk_cli/tests/test_contracts.py +++ b/multiversx_sdk_cli/tests/test_contracts.py @@ -3,14 +3,14 @@ import pytest from Cryptodome.Hash import keccak -from multiversx_sdk import Address +from multiversx_sdk import 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 (_interpret_as_number_if_safely, - _prepare_argument, - prepare_args_for_factory) +from multiversx_sdk_cli.contracts import (SmartContract, + _interpret_as_number_if_safely, + _prepare_argument) logging.basicConfig(level=logging.INFO) @@ -70,13 +70,14 @@ def test_interpret_as_number_if_safely(): def test_prepare_args_for_factories(): + sc = SmartContract(TransactionsFactoryConfig("mock")) args = [ "0x5", "123", "false", "true", "str:test-string", "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" ] - arguments = prepare_args_for_factory(args) + arguments = sc.prepare_args_for_factory(args) assert arguments[0] == b"\x05" assert arguments[1] == 123 assert arguments[2] == False From 83591d92d14aafed36deae63f73b0311ddf63e34 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 15:35:25 +0300 Subject: [PATCH 35/82] add check for token transfers --- 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 9ffcc048..0db9ab10 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -43,7 +43,7 @@ def do_prepare_transaction(args: Any) -> Transaction: native_amount = int(args.value) transfers = getattr(args, "token_transfers", []) - transfers = prepare_token_transfers(transfers) + transfers = prepare_token_transfers(transfers) if transfers else None config = TransactionsFactoryConfig(args.chain) factory = TransferTransactionsFactory(config) From 34433910b77fd3a39cbcac74ba40ea3a5c192fef Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 15:45:04 +0300 Subject: [PATCH 36/82] reference sdk-py from feat breanch --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ac423476..52bb6b78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ "semver", "requests-cache", "rich==13.3.4", - "multiversx-sdk>=0.9.2,<1.0.0", + "multiversx-sdk@git+https://github.com/multiversx/mx-sdk-py#egg=feat/next", "argcomplete==3.2.2" ] From d9cbc62c959661f06c6cd84af091216a7e656226 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 15:49:40 +0300 Subject: [PATCH 37/82] fix package reference --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 52bb6b78..2e18c427 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ "semver", "requests-cache", "rich==13.3.4", - "multiversx-sdk@git+https://github.com/multiversx/mx-sdk-py#egg=feat/next", + "multiversx-sdk@git+https://github.com/multiversx/mx-sdk-py#egg=multiversx_sdk@feat/next", "argcomplete==3.2.2" ] From 7410fee8868d1e94b60afb9f48eb28b208676622 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 15:52:43 +0300 Subject: [PATCH 38/82] fix package reference --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2e18c427..d37412bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ "semver", "requests-cache", "rich==13.3.4", - "multiversx-sdk@git+https://github.com/multiversx/mx-sdk-py#egg=multiversx_sdk@feat/next", + "multiversx-sdk@git+https://github.com/multiversx/mx-sdk-py@feat/next#egg=multiversx_sdk", "argcomplete==3.2.2" ] From bbfba048a3b1c51888e35cc1c5c87835fd405fce Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 15:54:11 +0300 Subject: [PATCH 39/82] allow direct references --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index d37412bb..7a00392d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,10 @@ classifiers = [ "Intended Audience :: Developers" ] + +[tool.hatch.metadata] +allow-direct-references = true + dependencies = [ "toml>=0.10.2", "requests", From 1fb13f371f88284b5272cb697190dc2a99d177ce Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 15:56:13 +0300 Subject: [PATCH 40/82] change packages order --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7a00392d..957535e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,8 +31,8 @@ dependencies = [ "semver", "requests-cache", "rich==13.3.4", - "multiversx-sdk@git+https://github.com/multiversx/mx-sdk-py@feat/next#egg=multiversx_sdk", - "argcomplete==3.2.2" + "argcomplete==3.2.2", + "multiversx-sdk@git+https://github.com/multiversx/mx-sdk-py@feat/next#egg=multiversx_sdk" ] [project.scripts] From a551af905006442f7f9c5d02e522eb4f191ba842 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 4 Jul 2024 16:58:41 +0300 Subject: [PATCH 41/82] reference beta release --- pyproject.toml | 6 +----- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 957535e8..a87f87be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,10 +19,6 @@ classifiers = [ "Intended Audience :: Developers" ] - -[tool.hatch.metadata] -allow-direct-references = true - dependencies = [ "toml>=0.10.2", "requests", @@ -32,7 +28,7 @@ dependencies = [ "requests-cache", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk@git+https://github.com/multiversx/mx-sdk-py@feat/next#egg=multiversx_sdk" + "multiversx-sdk==0.11.0b0" ] [project.scripts] diff --git a/requirements.txt b/requirements.txt index cadf372e..987c82e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,4 @@ requests-cache rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk @ git+https://github.com/multiversx/mx-sdk-py@feat/next +multiversx-sdk==0.11.0b0 From dbb68ac67cc8d05800da4403c5dec0d80e67c9fc Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 8 Jul 2024 10:49:52 +0300 Subject: [PATCH 42/82] fixes after review --- multiversx_sdk_cli/cli_contracts.py | 6 +++--- multiversx_sdk_cli/cli_delegation.py | 2 +- multiversx_sdk_cli/cli_dns.py | 2 +- multiversx_sdk_cli/cli_shared.py | 19 +++++++++---------- multiversx_sdk_cli/cli_transactions.py | 2 +- multiversx_sdk_cli/cli_validators.py | 2 +- multiversx_sdk_cli/transactions.py | 3 +-- 7 files changed, 17 insertions(+), 19 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 62cc983b..b1d513e6 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -75,7 +75,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_outfile_arg(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, with_guardian=True) + 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") @@ -92,7 +92,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_outfile_arg(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, with_guardian=True) + cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) _add_function_arg(sub) _add_arguments_arg(sub) _add_token_transfers_args(sub) @@ -113,7 +113,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: _add_metadata_arg(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, with_guardian=True) + 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") diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 9c49d158..7ce579f5 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -182,7 +182,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, with_guardian=True) + 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_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 a7c8dcd6..c22de0d6 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -20,7 +20,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_broadcast_args(sub, relay=True) 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, with_guardian=True) + cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) cli_shared.add_guardian_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_shared.py b/multiversx_sdk_cli/cli_shared.py index 6a155677..867c2e1f 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -69,9 +69,7 @@ def add_tx_args( with_nonce: bool = True, with_receiver: bool = True, with_data: bool = True, - with_estimate_gas: bool = False, - with_guardian: bool = False, - with_relayed_v3: bool = True): + with_estimate_gas: bool = False): 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)") @@ -93,17 +91,18 @@ def add_tx_args( 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)") - if with_guardian: - add_guardian_args(sub) - - if with_relayed_v3: - sub.add_argument("--relayer", help="the address of the relayer") - sub.add_argument("--inner-transactions", help="a json file containing the inner transactions; should only be provided when creating the relayer's transaction") - sub.add_argument("--inner-transactions-outfile", type=str, help="where to save the transaction as an inner transaction (default: stdout)") + add_guardian_args(sub) + add_relayed_v3_args(sub) sub.add_argument("--options", type=int, default=0, help="the transaction options (default: 0)") +def add_relayed_v3_args(sub: Any): + sub.add_argument("--relayer", help="the address of the relayer") + sub.add_argument("--inner-transactions", help="a json file containing the inner transactions; should only be provided when creating the relayer's transaction") + sub.add_argument("--inner-transactions-outfile", type=str, help="where to save the transaction as an inner transaction (default: stdout)") + + 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="") diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 9484fcf4..924e4c9d 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -64,7 +64,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: def _add_common_arguments(args: List[str], sub: Any): cli_shared.add_wallet_args(args, sub) - cli_shared.add_tx_args(args, sub, with_guardian=True) + cli_shared.add_tx_args(args, sub) sub.add_argument("--data-file", type=str, default=None, help="a file containing transaction data") diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index e2a73b54..106f403c 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -88,7 +88,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, with_guardian=True) + 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_outfile_arg(sub, what="signed transaction, hash") cli_shared.add_guardian_wallet_args(args, sub) diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 0db9ab10..d875be22 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -49,7 +49,6 @@ def do_prepare_transaction(args: Any) -> Transaction: factory = TransferTransactionsFactory(config) receiver = Address.new_from_bech32(args.receiver) - # temporary workaround until proper fix in sdk-py if native_amount or transfers: tx = factory.create_transaction_for_transfer( sender=account.address, @@ -231,7 +230,7 @@ def load_transaction_from_file(f: TextIO) -> Transaction: def load_inner_transactions_from_file(path: Path) -> List[Transaction]: - data_json = path.read_bytes() + data_json = path.read_text() transactions: List[Dict[str, Any]] = json.loads(data_json).get("innerTransactions") tx_converter = TransactionsConverter() From 405ee5f342cb2052c3e8f88a7893050a7d5dd7e7 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 9 Jul 2024 18:27:07 +0300 Subject: [PATCH 43/82] add support for contract abi --- multiversx_sdk_cli/cli_contracts.py | 44 +- multiversx_sdk_cli/contracts.py | 75 +- .../tests/test_cli_contracts.py | 37 + .../tests/testdata/deploy_multisig_args.json | 12 + .../tests/testdata/multisig.abi.json | 1302 +++++++++++++++++ .../tests/testdata/multisig.wasm | Bin 0 -> 24802 bytes 6 files changed, 1416 insertions(+), 54 deletions(-) create mode 100644 multiversx_sdk_cli/tests/testdata/deploy_multisig_args.json create mode 100644 multiversx_sdk_cli/tests/testdata/multisig.abi.json create mode 100644 multiversx_sdk_cli/tests/testdata/multisig.wasm diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 94f017c7..88460db0 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 +from typing import Any, List, Tuple from multiversx_sdk import (Address, AddressComputer, ProxyNetworkProvider, Transaction, TransactionsFactoryConfig) @@ -73,6 +73,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: 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) cli_shared.add_outfile_arg(sub) cli_shared.add_wallet_args(args, sub) @@ -111,6 +112,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: 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) _add_bytecode_arg(sub) _add_metadata_arg(sub) @@ -323,15 +325,16 @@ def deploy(args: Any): sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) - contract = SmartContract(config) + abi = Abi.load(Path(args.abi)) if args.abi else None + contract = SmartContract(config, abi) - address_computer = AddressComputer(NUMBER_OF_SHARDS) - contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=args.nonce) + arguments, args_from_file = _get_contract_arguments(args) tx = contract.prepare_deploy_transaction( owner=sender, bytecode=Path(args.bytecode), - arguments=args.arguments, + arguments=arguments, + args_from_file=args_from_file, upgradeable=args.metadata_upgradeable, readable=args.metadata_readable, payable=args.metadata_payable, @@ -344,6 +347,9 @@ def deploy(args: Any): guardian=args.guardian) tx = _sign_guarded_tx(args, tx) + address_computer = AddressComputer(NUMBER_OF_SHARDS) + contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=args.nonce) + logger.info("Contract address: %s", contract_address.to_bech32()) utils.log_explorer_contract_address(args.chain, contract_address.to_bech32()) @@ -377,12 +383,7 @@ def call(args: Any): abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) - json_args = json.loads(Path(args.arguments_file).expanduser().read_text()) if args.arguments_file else None - - if json_args and args.arguments: - raise Exception("Both '--arguments' and '--arguments-json' provided.") - - arguments = json_args or args.arguments + arguments, args_from_file = _get_contract_arguments(args) contract_address = Address.new_from_bech32(args.contract) tx = contract.prepare_execute_transaction( @@ -390,6 +391,7 @@ def call(args: Any): contract=contract_address, function=args.function, arguments=arguments, + args_from_file=args_from_file, gas_limit=int(args.gas_limit), value=int(args.value), transfers=args.token_transfers, @@ -411,14 +413,18 @@ def upgrade(args: Any): sender = cli_shared.prepare_account(args) config = TransactionsFactoryConfig(args.chain) - contract = SmartContract(config) + abi = Abi.load(Path(args.abi)) if args.abi else None + contract = SmartContract(config, abi) + + arguments, args_from_file = _get_contract_arguments(args) contract_address = Address.new_from_bech32(args.contract) tx = contract.prepare_upgrade_transaction( owner=sender, contract=contract_address, bytecode=Path(args.bytecode), - arguments=args.arguments, + arguments=arguments, + args_from_file=args_from_file, upgradeable=args.metadata_upgradeable, readable=args.metadata_readable, payable=args.metadata_payable, @@ -451,6 +457,18 @@ def query(args: Any): utils.dump_out_json(result) +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: + raise Exception("Both '--arguments' and '--arguments-file' provided.") + + if json_args: + return json_args, True + else: + return args.arguments, False + + def _send_or_simulate(tx: Transaction, contract_address: IAddress, args: Any): output_builder = cli_shared.send_or_simulate(tx, args, dump_output=False) output_builder.set_contract_address(contract_address) diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 754130fd..e6dcafa8 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -1,7 +1,7 @@ import base64 import logging from pathlib import Path -from typing import Any, Dict, List, Optional, Protocol, Sequence, Union, cast +from typing import Any, List, Optional, Protocol, Sequence, Union from multiversx_sdk import (Address, SmartContractTransactionsFactory, Token, TokenComputer, TokenTransfer, Transaction, @@ -81,7 +81,8 @@ def __init__(self, config: IConfig, abi: Optional[Abi] = None): def prepare_deploy_transaction(self, owner: Account, bytecode: Path, - arguments: Union[List[str], None], + arguments: Union[List[Any], None], + args_from_file: bool, upgradeable: bool, readable: bool, payable: bool, @@ -92,7 +93,9 @@ def prepare_deploy_transaction(self, version: int, options: int, guardian: str) -> Transaction: - args = self.prepare_args_for_factory("constructor", arguments) if arguments else [] + args = arguments if arguments else [] + if not args_from_file: + args = self.prepare_args_for_factory(args) tx = self._factory.create_transaction_for_deploy( sender=owner.address, @@ -117,7 +120,8 @@ def prepare_execute_transaction(self, caller: Account, contract: Address, function: str, - arguments: Any, + arguments: Union[List[Any], None], + args_from_file: bool, gas_limit: int, value: int, transfers: Union[List[str], None], @@ -126,7 +130,10 @@ def prepare_execute_transaction(self, options: int, guardian: str) -> Transaction: token_transfers = self._prepare_token_transfers(transfers) if transfers else [] - args = self.prepare_args_for_factory(function, arguments) if arguments else [] + + args = arguments if arguments else [] + if not args_from_file: + args = self.prepare_args_for_factory(args) tx = self._factory.create_transaction_for_execute( sender=caller.address, @@ -150,6 +157,7 @@ def prepare_upgrade_transaction(self, contract: IAddress, bytecode: Path, arguments: Union[List[str], None], + args_from_file: bool, upgradeable: bool, readable: bool, payable: bool, @@ -160,7 +168,11 @@ def prepare_upgrade_transaction(self, version: int, options: int, guardian: str) -> Transaction: - args = self.prepare_args_for_factory("upgrade_constructor", arguments) if arguments else [] + args = self.prepare_args_for_factory(arguments) if arguments else [] + + args = arguments if arguments else [] + if not args_from_file: + args = self.prepare_args_for_factory(args) tx = self._factory.create_transaction_for_upgrade( sender=owner.address, @@ -197,46 +209,27 @@ def _prepare_token_transfers(self, transfers: List[str]) -> List[TokenTransfer]: return token_transfers - def prepare_args_for_factory(self, endpoint: str, arguments: Any) -> List[Any]: + def prepare_args_for_factory(self, arguments: List[str]) -> List[Any]: args: List[Any] = [] - if isinstance(arguments, dict): - return self._prepare_args_using_abi(endpoint, arguments) - - if isinstance(arguments, list): - for arg in arguments: # type: ignore - arg = cast(str, arg) - if arg.startswith(HEX_PREFIX): - args.append(hex_to_bytes(arg)) - elif arg.isnumeric(): - args.append(int(arg)) - elif arg.startswith(DEFAULT_HRP): - args.append(Address.new_from_bech32(arg)) - elif arg.lower() == FALSE_STR_LOWER: - args.append(False) - elif arg.lower() == TRUE_STR_LOWER: - args.append(True) - elif arg.startswith(STR_PREFIX): - args.append(arg[len(STR_PREFIX):]) - else: - raise errors.BadUserInput(f"Unknown argument type for argument: `{arg}`. Use `mxpy contract --help` to check all supported arguments") + for arg in arguments: + if arg.startswith(HEX_PREFIX): + args.append(hex_to_bytes(arg)) + elif arg.isnumeric(): + args.append(int(arg)) + elif arg.startswith(DEFAULT_HRP): + args.append(Address.new_from_bech32(arg)) + elif arg.lower() == FALSE_STR_LOWER: + args.append(False) + elif arg.lower() == TRUE_STR_LOWER: + args.append(True) + elif arg.startswith(STR_PREFIX): + args.append(arg[len(STR_PREFIX):]) + else: + raise errors.BadUserInput(f"Unknown argument type for argument: `{arg}`. Use `mxpy contract --help` to check all supported arguments") return args - def _prepare_args_using_abi(self, function: str, arguments: Dict[str, Any]): - if not self._abi: - raise Exception("Abi file not provided") - - endpoint_definition = [endpoint for endpoint in self._abi.definition.endpoints if endpoint.name == function] - if not endpoint_definition: - raise Exception(f"Endpoint [{function}] not found") - elif len(endpoint_definition) > 1: - raise Exception(f"More than one endpoint with name [{function}] found.") - else: - endpoint_definition = endpoint_definition[0] - - input_parameters_names = [input.name for input in endpoint_definition.inputs] - def query_contract( contract_address: IAddress, diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index b69b9867..94ec42be 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -317,6 +317,43 @@ def test_contract_commands_argument_parameter(): assert not return_code +def test_contract_deploy_with_abi(capsys: Any): + alice = f"{parent}/testdata/alice.pem" + 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" + ]) + 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" + ]) + assert not return_code + + deploy_with_abi_data = get_transaction_data(capsys) + + assert deploy_without_abi_data == deploy_with_abi_data + + def _read_stdout(capsys: Any) -> str: return capsys.readouterr().out.strip() diff --git a/multiversx_sdk_cli/tests/testdata/deploy_multisig_args.json b/multiversx_sdk_cli/tests/testdata/deploy_multisig_args.json new file mode 100644 index 00000000..c70f879f --- /dev/null +++ b/multiversx_sdk_cli/tests/testdata/deploy_multisig_args.json @@ -0,0 +1,12 @@ +[ + 2, + [ + { + "bech32": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + }, + { + "bech32": "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + } + + ] +] diff --git a/multiversx_sdk_cli/tests/testdata/multisig.abi.json b/multiversx_sdk_cli/tests/testdata/multisig.abi.json new file mode 100644 index 00000000..2493cc01 --- /dev/null +++ b/multiversx_sdk_cli/tests/testdata/multisig.abi.json @@ -0,0 +1,1302 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.80.0-nightly", + "commitHash": "791adf759cc065316f054961875052d5bc03e16c", + "commitDate": "2024-05-21", + "channel": "Nightly", + "short": "rustc 1.80.0-nightly (791adf759 2024-05-21)" + }, + "contractCrate": { + "name": "multisig", + "version": "1.0.0", + "gitVersion": "v0.45.2.1-reproducible-309-ge0b3bbb" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.50.4" + } + }, + "docs": [ + "Multi-signature smart contract implementation.", + "Acts like a wallet that needs multiple signers for any action performed.", + "See the readme file for more detailed documentation." + ], + "name": "Multisig", + "constructor": { + "inputs": [ + { + "name": "quorum", + "type": "u32" + }, + { + "name": "board", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, + "endpoints": [ + { + "docs": [ + "Allows the contract to receive funds even if it is marked as unpayable in the protocol." + ], + "name": "deposit", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [], + "outputs": [] + }, + { + "docs": [ + "Clears storage pertaining to an action that is no longer supposed to be executed.", + "Any signatures that the action received must first be removed, via `unsign`.", + "Otherwise this endpoint would be prone to abuse." + ], + "name": "discardAction", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Discard all the actions with the given IDs" + ], + "name": "discardBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "action_ids", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Minimum number of signatures needed to perform any action." + ], + "name": "getQuorum", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Denormalized board member count.", + "It is kept in sync with the user list by the contract." + ], + "name": "getNumBoardMembers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "getNumGroups", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Denormalized proposer count.", + "It is kept in sync with the user list by the contract." + ], + "name": "getNumProposers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "getActionGroup", + "mutability": "readonly", + "inputs": [ + { + "name": "group_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "getLastGroupActionId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "The index of the last proposed action.", + "0 means that no action was ever proposed yet." + ], + "name": "getActionLastIndex", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Initiates board member addition process.", + "Can also be used to promote a proposer to board member." + ], + "name": "proposeAddBoardMember", + "mutability": "mutable", + "inputs": [ + { + "name": "board_member_address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Initiates proposer addition process..", + "Can also be used to demote a board member to proposer." + ], + "name": "proposeAddProposer", + "mutability": "mutable", + "inputs": [ + { + "name": "proposer_address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Removes user regardless of whether it is a board member or proposer." + ], + "name": "proposeRemoveUser", + "mutability": "mutable", + "inputs": [ + { + "name": "user_address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "proposeChangeQuorum", + "mutability": "mutable", + "inputs": [ + { + "name": "new_quorum", + "type": "u32" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Propose a transaction in which the contract will perform a transfer-execute call.", + "Can send EGLD without calling anything.", + "Can call smart contract endpoints directly.", + "Doesn't really work with builtin functions." + ], + "name": "proposeTransferExecute", + "mutability": "mutable", + "inputs": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "egld_amount", + "type": "BigUint" + }, + { + "name": "opt_gas_limit", + "type": "Option" + }, + { + "name": "function_call", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "proposeTransferExecuteEsdt", + "mutability": "mutable", + "inputs": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "tokens", + "type": "List" + }, + { + "name": "opt_gas_limit", + "type": "Option" + }, + { + "name": "function_call", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Propose a transaction in which the contract will perform an async call call.", + "Can call smart contract endpoints directly.", + "Can use ESDTTransfer/ESDTNFTTransfer/MultiESDTTransfer to send tokens, while also optionally calling endpoints.", + "Works well with builtin functions.", + "Cannot simply send EGLD directly without calling anything." + ], + "name": "proposeAsyncCall", + "mutability": "mutable", + "inputs": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "egld_amount", + "type": "BigUint" + }, + { + "name": "opt_gas_limit", + "type": "Option" + }, + { + "name": "function_call", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "proposeSCDeployFromSource", + "mutability": "mutable", + "inputs": [ + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "source", + "type": "Address" + }, + { + "name": "code_metadata", + "type": "CodeMetadata" + }, + { + "name": "arguments", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "proposeSCUpgradeFromSource", + "mutability": "mutable", + "inputs": [ + { + "name": "sc_address", + "type": "Address" + }, + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "source", + "type": "Address" + }, + { + "name": "code_metadata", + "type": "CodeMetadata" + }, + { + "name": "arguments", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "proposeBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "actions", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Used by board members to sign actions." + ], + "name": "sign", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Sign all the actions in the given batch" + ], + "name": "signBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "group_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "signAndPerform", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "optional
", + "multi_result": true + } + ] + }, + { + "name": "signBatchAndPerform", + "mutability": "mutable", + "inputs": [ + { + "name": "group_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Board members can withdraw their signatures if they no longer desire for the action to be executed.", + "Actions that are left with no valid signatures can be then deleted to free up storage." + ], + "name": "unsign", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Unsign all actions with the given IDs" + ], + "name": "unsignBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "group_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Returns `true` (`1`) if the user has signed the action.", + "Does not check whether or not the user is still a board member and the signature valid." + ], + "name": "signed", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + }, + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "name": "unsignForOutdatedBoardMembers", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + }, + { + "name": "outdated_board_members", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Returns `true` (`1`) if `getActionValidSignerCount >= getQuorum`." + ], + "name": "quorumReached", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "docs": [ + "Proposers and board members use this to launch signed actions." + ], + "name": "performAction", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "optional
", + "multi_result": true + } + ] + }, + { + "docs": [ + "Perform all the actions in the given batch" + ], + "name": "performBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "group_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "dnsRegister", + "onlyOwner": true, + "mutability": "mutable", + "payableInTokens": [ + "EGLD" + ], + "inputs": [ + { + "name": "dns_address", + "type": "Address" + }, + { + "name": "name", + "type": "bytes" + } + ], + "outputs": [] + }, + { + "docs": [ + "Iterates through all actions and retrieves those that are still pending.", + "Serialized full action data:", + "- the action id", + "- the serialized action data", + "- (number of signers followed by) list of signer addresses." + ], + "name": "getPendingActionFullInfo", + "mutability": "readonly", + "inputs": [ + { + "name": "opt_range", + "type": "optional>", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ], + "labels": [ + "multisig-external-view" + ], + "allow_multiple_var_args": true + }, + { + "docs": [ + "Indicates user rights.", + "`0` = no rights,", + "`1` = can propose, but not sign,", + "`2` = can propose and sign." + ], + "name": "userRole", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], + "outputs": [ + { + "type": "UserRole" + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "Lists all users that can sign actions." + ], + "name": "getAllBoardMembers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "Lists all proposers that are not board members." + ], + "name": "getAllProposers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "Serialized action data of an action with index." + ], + "name": "getActionData", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "Action" + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "Gets addresses of all users who signed an action.", + "Does not check if those users are still board members or not,", + "so the result may contain invalid signers." + ], + "name": "getActionSigners", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "List
" + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "Gets addresses of all users who signed an action and are still board members.", + "All these signatures are currently valid." + ], + "name": "getActionSignerCount", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "u32" + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "It is possible for board members to lose their role.", + "They are not automatically removed from all actions when doing so,", + "therefore the contract needs to re-check every time when actions are performed.", + "This function is used to validate the signers before performing an action.", + "It also makes it easy to check before performing an action." + ], + "name": "getActionValidSignerCount", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "u32" + } + ], + "labels": [ + "multisig-external-view" + ] + } + ], + "events": [ + { + "identifier": "asyncCallSuccess", + "inputs": [ + { + "name": "results", + "type": "variadic", + "indexed": true + } + ] + }, + { + "identifier": "asyncCallError", + "inputs": [ + { + "name": "err_code", + "type": "u32", + "indexed": true + }, + { + "name": "err_message", + "type": "bytes", + "indexed": true + } + ] + }, + { + "identifier": "startPerformAction", + "inputs": [ + { + "name": "data", + "type": "ActionFullInfo" + } + ] + }, + { + "identifier": "performChangeUser", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "changed_user", + "type": "Address", + "indexed": true + }, + { + "name": "old_role", + "type": "UserRole", + "indexed": true + }, + { + "name": "new_role", + "type": "UserRole", + "indexed": true + } + ] + }, + { + "identifier": "performChangeQuorum", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "new_quorum", + "type": "u32", + "indexed": true + } + ] + }, + { + "identifier": "performAsyncCall", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "to", + "type": "Address", + "indexed": true + }, + { + "name": "egld_value", + "type": "BigUint", + "indexed": true + }, + { + "name": "gas", + "type": "u64", + "indexed": true + }, + { + "name": "endpoint", + "type": "bytes", + "indexed": true + }, + { + "name": "arguments", + "type": "variadic", + "indexed": true + } + ] + }, + { + "identifier": "performTransferExecuteEgld", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "to", + "type": "Address", + "indexed": true + }, + { + "name": "egld_value", + "type": "BigUint", + "indexed": true + }, + { + "name": "gas", + "type": "u64", + "indexed": true + }, + { + "name": "endpoint", + "type": "bytes", + "indexed": true + }, + { + "name": "arguments", + "type": "variadic", + "indexed": true + } + ] + }, + { + "identifier": "performTransferExecuteEsdt", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "to", + "type": "Address", + "indexed": true + }, + { + "name": "tokens", + "type": "List", + "indexed": true + }, + { + "name": "gas", + "type": "u64", + "indexed": true + }, + { + "name": "endpoint", + "type": "bytes", + "indexed": true + }, + { + "name": "arguments", + "type": "variadic", + "indexed": true + } + ] + }, + { + "identifier": "performDeployFromSource", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "egld_value", + "type": "BigUint", + "indexed": true + }, + { + "name": "source_address", + "type": "Address", + "indexed": true + }, + { + "name": "code_metadata", + "type": "CodeMetadata", + "indexed": true + }, + { + "name": "gas", + "type": "u64", + "indexed": true + }, + { + "name": "arguments", + "type": "variadic", + "indexed": true + } + ] + }, + { + "identifier": "performUpgradeFromSource", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "target_address", + "type": "Address", + "indexed": true + }, + { + "name": "egld_value", + "type": "BigUint", + "indexed": true + }, + { + "name": "source_address", + "type": "Address", + "indexed": true + }, + { + "name": "code_metadata", + "type": "CodeMetadata", + "indexed": true + }, + { + "name": "gas", + "type": "u64", + "indexed": true + }, + { + "name": "arguments", + "type": "variadic", + "indexed": true + } + ] + } + ], + "esdtAttributes": [], + "hasCallback": true, + "types": { + "Action": { + "type": "enum", + "variants": [ + { + "name": "Nothing", + "discriminant": 0 + }, + { + "name": "AddBoardMember", + "discriminant": 1, + "fields": [ + { + "name": "0", + "type": "Address" + } + ] + }, + { + "name": "AddProposer", + "discriminant": 2, + "fields": [ + { + "name": "0", + "type": "Address" + } + ] + }, + { + "name": "RemoveUser", + "discriminant": 3, + "fields": [ + { + "name": "0", + "type": "Address" + } + ] + }, + { + "name": "ChangeQuorum", + "discriminant": 4, + "fields": [ + { + "name": "0", + "type": "u32" + } + ] + }, + { + "name": "SendTransferExecuteEgld", + "discriminant": 5, + "fields": [ + { + "name": "0", + "type": "CallActionData" + } + ] + }, + { + "name": "SendTransferExecuteEsdt", + "discriminant": 6, + "fields": [ + { + "name": "0", + "type": "EsdtTransferExecuteData" + } + ] + }, + { + "name": "SendAsyncCall", + "discriminant": 7, + "fields": [ + { + "name": "0", + "type": "CallActionData" + } + ] + }, + { + "name": "SCDeployFromSource", + "discriminant": 8, + "fields": [ + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "source", + "type": "Address" + }, + { + "name": "code_metadata", + "type": "CodeMetadata" + }, + { + "name": "arguments", + "type": "List" + } + ] + }, + { + "name": "SCUpgradeFromSource", + "discriminant": 9, + "fields": [ + { + "name": "sc_address", + "type": "Address" + }, + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "source", + "type": "Address" + }, + { + "name": "code_metadata", + "type": "CodeMetadata" + }, + { + "name": "arguments", + "type": "List" + } + ] + } + ] + }, + "ActionFullInfo": { + "type": "struct", + "docs": [ + "Not used internally, just to retrieve results via endpoint." + ], + "fields": [ + { + "name": "action_id", + "type": "u32" + }, + { + "name": "group_id", + "type": "u32" + }, + { + "name": "action_data", + "type": "Action" + }, + { + "name": "signers", + "type": "List
" + } + ] + }, + "ActionStatus": { + "type": "enum", + "variants": [ + { + "name": "Available", + "discriminant": 0 + }, + { + "name": "Aborted", + "discriminant": 1 + } + ] + }, + "CallActionData": { + "type": "struct", + "fields": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "egld_amount", + "type": "BigUint" + }, + { + "name": "opt_gas_limit", + "type": "Option" + }, + { + "name": "endpoint_name", + "type": "bytes" + }, + { + "name": "arguments", + "type": "List" + } + ] + }, + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + }, + "EsdtTransferExecuteData": { + "type": "struct", + "fields": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "tokens", + "type": "List" + }, + { + "name": "opt_gas_limit", + "type": "Option" + }, + { + "name": "endpoint_name", + "type": "bytes" + }, + { + "name": "arguments", + "type": "List" + } + ] + }, + "UserRole": { + "type": "enum", + "variants": [ + { + "name": "None", + "discriminant": 0 + }, + { + "name": "Proposer", + "discriminant": 1 + }, + { + "name": "BoardMember", + "discriminant": 2 + } + ] + } + } +} diff --git a/multiversx_sdk_cli/tests/testdata/multisig.wasm b/multiversx_sdk_cli/tests/testdata/multisig.wasm new file mode 100644 index 0000000000000000000000000000000000000000..e7cc7b726e2d3456bea28923b59395b361b8f116 GIT binary patch literal 24802 zcmcJX3w#|{edp)Q%)PqOmFyEo1jRNm_hMQF?8I$@?G2P_5N)0EM*M^_IeRq3yEtK_A-|3WZX(??;-xcKh0vZnwK#TA<(G z|IEz2k`$EGqZPUe!jC5B`PahGC*i)b6<0^(>-!(YH8-~6F|MxSzZp)Z3>vs zSYy0=YJQ~?hS}7EBVRhXa;CG;4Oo;F0kb|}Hk}E;9OSiz^%WFX)f+W80{vYpXUa~?fqd7M;|BVbU87+QIaWZ!oOdtHo}7g}M<2Xx3Njl^ z*Y?m83P6UhQa6yLa*I`85NVggeq*T!Lwb z7N-iiC}Alobm zH3R_ygEkrRmPz#f9$D)O0u6s8(6g>fW;_m!@Vq z>ooIf)-Iaexia6KErHl~CExaL3_xq&ogKo_M^IuNBU8XD4k+%+j2W&UTmXhTgixQKr>Zj&f+ zwE;JfMEtnK#b5k?0|QA?O8~fjU2@Uomo7D$7o+@VpR&s*xD5P8{l@y4&Y8uf^Kq17 zle*Dwx`CCz`{=jaz)a`t;xeVb?M7y1m#48N`E+-7aUuF0H(V4sN3a`codl>G40BD1K;AGi%P2~$|B z=xH}f$)Ty`u04mUgEP?|+7#hYwFehwIuAyFV=oDI_!vhu%yITVD!|Bq|VEG*yCIXS!Bh2a0-#$fSb7R}j(lcCH06|Ej8 z7Nh^`8Y?j6p2hi2^y$Fz`T0J^E=T`pQk2inAFib6pWH|#MYm3Mr=rg|Ulkk?T8P?b zO|Zgrp=|Hs3M<~{-1Svktm^Dcf90jPF^%DvoSHrzy(S(yb_{Y%9Ru>jHIE%T)tNeb z?D*7jCy9st;vHC)>zupjZ)m&8$VZvuFY^30uctV_^rnXXjmJ^$PPe07ag;^PEozqB zkxuv*Ma_(dU2)1KrncFP@=v|7k#F$ae=c#!#pg%)&eQE=8_gKS?`XwYG#-yCd=J*+&^Iw%EHzZ?+_oWk=rn`CAHKErHNkc!Lk{4_6jGJ8yvrPc(9!>S?YCKrN4`nn(gEmF4;nlso^#ZE#cqd5SLc<$D z%8<+gl3NYQogg_T>0%T}TVq zCCetwX*&g?c3qO)xHG+A5)ST-xyfiZqb7ENOmIKRy?R{%PQ$$uTRv zbm<>9J~Y{Qp}4ems{lcaC3@&E(j1%G=c987bPOVb6{ZNEVOOI%lf*Tf zLlT9rQ*ra=HR6X1^@RA`(o5V1A((HN!y2s;Mnow-*L)1Wt51*+owSWnnro*fMt7ta z^9YfU)R>7G)*bK%C6EY7Xkz9_B{Z=J7WW7SLIuI7fnY62-fTf9DW+r$W37=*le%$w zF}~2#Naw%0m1Hh|$7>tdo0lP>%;Uc%i{Wgmu1ljeP}NT$x>B87ghhBoSfo?Nkr`wh zqig`7;Zm2##9`76kTTDTIbPiy=1~Om6aIYjP8VN@C-hg%aytKc;PfBwyV}6928Pj~amv z7fL$#H@d4yAh~6rS25xX4R!yjwMa9|T6Dj@(j>Pn5{aOm^eO0&3pzxRbO_=}hg{Gh zSwf>FH^Y91zg-k`rbSr{A+OPja6_AA9g8rq8E0bu8g+6KXrh2}OQiw*-3G-ZnAo@q0f7~^1t)P;%M-ipg zx{+F&EZvnQH@OWhl}VQno`;9Eig9yEH|Lg`J5>s8Y+i}@C5L@sam_#Qnnu1+A}sD0 zRKOTDC#qSYJ47gEb-|b%&Rm$$J>!hq*PF4J4MABwTa(6y%E)HK%{X$TWRMX@42qir zH1oC1&95*D`!BJ6jSkRf23`O z&m%2q{!xk&yOmY78^0lH-X!eA3YMU^Jtc?tE_emg^1q|QH3hE{6DD}x+^*RVwi4uD zy{bMJv!o_}$q0K)@{iP4Htw7J%k3fmyT=+hY2Vy5v5l}vGWgXX6Vf?zOj zl57Iq`T#c-Z-1e+&sI7$9sVMk;=pH)b*`PtaY}oR6St+~q}!h3l=eAJX`kbiR*n-x znd5}{F@j3kilw*HZ(`x{@EnFK^7kTe|6~lm6CMD%H~9C9usfoy=|ukKry8bR`FH8& zmI$pb>}#~K8T#gLKx8+F{@WbXj{L@UBFHcKZzvd4HOP`H&7W{^#oPc~r(k$6uCc#2 zo`{*Y4+aEMWh#~ZeU|~+zd&2bRSR_0)Yj-~zyl+a?`OmciuKm=0!J_mQ&b2@Z~Fv8 z6j3mcSTG>*@<{X!$_lza0t(PiLjzuySrRrWY95XJHCY75DSQis*Q>Cv`5~(b zR*4z{ZieyK$lyLtM$GwJw6;k|gjS#1hsSN0muxnJvEG_4{;pZmO%R4z2SS(^{9^Np zA9wHZo6OeB<~S`C%`Hf`DgCwi4}-!GCfFf|76iVHgrQhQR=}dbe0&b^w16pQSoE*B zADe3vZJ6h%3-Pi6MU;Rm3S;ujzYst{Q4xsTc12h?o4eu+{1t?w&$}a0nlKlZ77w6S zD<9;zd9{g)kd@ZRK!OyE=FtHhf&L&xrXI1t1CRxV$~VM~-I`nuyej!$29!Z?MfB6~ zU*5M6UZ;idu1aK!RX$ieQ>oOm$_E2nt@2;M0N)vsEM-t#I2^DZFxqlbzr_fYI~frR zZVW*=97e`%vKzr6!A1Dm3SU!>)uZ9cFI>k~; zBEUa;mD{yXLc1Aqd2pfZ~hsL>BFc8j?A~ zv?jU8-}Le5ju9u@xFd?V!CFclBD0nx3WAzv_53o4EgB<1H6%zV=_g1K!>(^=aguI{ z8Z6pil)!Ke50#lwa_0_?Nbd{;=&mW|FycZf@vn{ZNb5Oz$q<=>e$nKCW?^GCHkC}% zzshSGld$M1Kk3Id^HV3H$>%HAIiL5}Z!X;^?#$Om86R=YJ(Y-=ib;JCQq2iN{f{|7 zLK5sJ^+y<{kc8&z$lT-6z=Owel2Jzd;Z|f6pIG~w#p~tgqR9vdNYMFvaHSOPqc13f zaxWl!*^#NUD&^|dRt4$Y&7I4V=V;OPQ+*w zEgeoK007%4*hx@g{z8HhILXB-Gpk$<#Ths6b^PoxlmCVDWemAf*^;XgB!~%!m`_hw zw0?r56+D&qIP%b?t5Wze3S8V3(*&R)K6|a(*ZiYeJQ#e!PeiBNg;mIsedtS!>}JWq zktm*kXbC;Y1JQ@7#J|Q_{Gz2DMcl5;+%9m&nD2_Wg{Mq)gzanPJv>vnb*>D=hlX4) zHJw)hdG2s%t=-;M19{Km{^x;ap7cN0n|WG3zl!$cq*Evv2AGstIwV(QJie`kWQ@nR zVnex1w1#wfX^ZCL@gDLsTyAa+=(4jlnAci0C9?$vhIcQm9l?KM8IOr9!EP2+N>w;z z4@SVBoW`#BrQDFt*~4XIcY?$kK>j3VovcV=@*sC9jF9+!T3mg5WHNj73gC=fY1Q+( zP(t=wgHU}xkSG2CT}je>DUcfgxwx4(3c9riNX0a=0ni-e_wLp(zXw_aK$;JhZR*sr z8qji(-@Q<(ZY}GyWM=?i;XJwi1DNr2N3iX>w7m+o@^-RtJha@SHMBKKL*lG&ioxmTbe z?h2dd52bD5l;4FJmmw6{6`P#*?=QzBVn@Pp3P4OFzH zEmizp3|1_lk1}dm#G7OZy6d{H#y-&x--^dpe+OR{Ta3l zm|4E#713Io9JXmNLgq9`JZaG8Xa(wfda#c53MW739_m%}`hFdKHdHd7uI&NK_<)8}$q>x23_=Z7;7B^%>l# z&)`Ot!DT4RE8*0mHo?AETTS0m#Y#q5sE7A}Dj6-L`bi!u!)^)m2P|e6&RUXK-XVg* z+0VbJ{jDcSwoX5#^reSTv zFTl!${>UbjZ2q}Ym+=KzkN@}ecF&_)9*}}|-NfiJd$JMIo0AC4YKLWZCwD%2MT^mv zHjeEa#c5VA3hYr$aMD7Pvg8VAjGd!P=m6^HiZRLpa=tcbLb$@9?(i0JHm8d$bkM=D zTfRAuZf_n&0>yXYXnby2Qh{bG*&TAiGbCXm4+^hXNZSOfrevVEhSD(5yNf1$ z1N{_5*H#13;S&^$f`)+Lmdsi6I6IVx5Crr=?iAaNOQ}g>$#<7uXNM5RsKXK8N$n%&hpk;+=rPWjh6ei8w1^G?`;Z($C+aPK|vdKwKDI z{}VP46!uS;`{44M5i}luS}sED-)Sps{(X-$@@I+v`O^KFzGn+&;ohz&_9^Q10jommp*2SNER0ivt*ufbJYt~T!x|j`JA=mkrPq_m* zdb~y0mQBCe3;=_sc?zzyC8X`!X$ZTDW$R;2HCMEXCm{(Ss)r@`Ld5o324!91bhqw; z-VV<(oH?ssu-odlAfiG|(Nb8xxrio|b1Boe+<{}TEPr1pJGr=^Vc5-J z!?3qSgvGIiQtg5uZ#9*}Dr!{HZI*gUCP2QhjoV7w2r}qh7WDM3$PmDjprF60y(O@$ zJELf@ZBRq1zyR+z?T+@z9FI$}MG_17hNGtbU^7EaDp?DtA5>sq!zPLfW6jJkn3Y)8 zT_*VLijhNx?*ksSu^q#ABHiZRnz59KU@;*cv%#@9I3ZP-I?K%^rKyYZ4|p4pBJe@Z{Vzb0oP^COJ01#4n7M`#)gBOoZj)>(4wn3U7gz4KA)Rk_FeFwkFV5fW zigfEvI}b{LwTC@m=`maW=R+5muy-&RUfIXVIKLY37T*UVTa3 zlGUT{IDHv}S+=wC?~0!g2!KSCZDhHuD+#?zx(@A59^}ICXX#KEQg-BYHL?RL*TGYf zY+Q&2_X7pK$qi{X9r(G;KxSlyGWv4?V1duTLc5`Hq+k{4HO@E&l9JH?w*~}t^n?Mm zPXXu}V)eLW&jdGsY1~1^)#C9HWfm|KyW&@JK?hzzC8m?{b0~(H9B1!^Dx~AS{Ee4o zX)r93>w~-EQy8SW-;R6(%_%H^^S^>7wQBJ?5?Pk@BvWUSBV~YxK`{TYNz1qnl!oJO z6Q?Q7de3_V_?VvCP#-N33w;}g?97HZCh7mPYYjH@KL*In0EzNB|L@K5k$7Aw?r!z( z(%qo{w?)BiMOFUGNXWmV&}R*W?hpB``$n4ikC19Kr|C-w^k+(-rS|}B z0wbW6vcdj?E3mAiCqEyVq5uj4$axwm42!8RV1@hxdhr+m|Jvkr0MR^}l zy9J0ERPL7K5vL)@Fk&kCMrfrs*bK~$1XTK7d(UL{$mEvGyW>~WMIx-^Mw@|%qJVkD z-xJ17k#UP8AdPJh$!Dd6-6&;DW!~+aXpsOgv}_qLM+5gXl9MJNhPmMOSv0UJLycr6hn!PjoGM8C4gR?Vnrs2+(7&vQ!7v&Osh2Gg?y@A@jFq5#6i~)i zfzk*t3j*;)&_v>k^f1xJH3A-2|Y4*=SXTx6C*(fYk0>=fs*V4`SjR{()z=B@qL_VSp!mUanF%{&eIHSPEk{a3$>!o#K%n&2$ zq?@x7S$TXgxQ~|pt0HUpfk5#(N3af1K@cAXGq5$Mk603uq<|X(MJ$QnS}>Vulj>tRy7XlULS=^V zRprZKb;O%iluRHXz4J)Hu_bMQVU`xey$COhI8vq%UjZr=+WFDLyO}~ZS)8{c$#Q-J zQc3d{6Lw$1aU=|)ZxNU|znGJ{!9T70S!B!H|7~2h&E;>QwC0bCmGb+}z$t(o0~@FY z_P8$FX^BlA=UJzaaLZ#*2!Do*B4Hbin1xZiaN&wKWV;)64pQQ_A_&XQdLGK-v~|E&QPj9@0pqRV{opMB;`N-t&RL;c2^me2D}60g=QT(ak;%e89|lv z3o!M^2n7fIEEAF6L1lmylUyi_vXX!EL?gdhBl}+$IF_<_ST+k032UZ_h+=o+vcz&y4V7XsDzR8^0%@e0Lzeq-bRv83Kf*+;&Z<+8_UmC6iRpsrtRTX?7=}{XWwua@ zr=QW&rs668aWa!64f`9gh%7fxtVTiPFc`JWh8h%!h9xx#ba4krjWScVSV-Mgv zDtKQEyqhd03E=ji`Y)PvLkZ(Yi!*|pT^h~*;IW2(D5hy1=RW_1>Io~De;_=4O@J2j zXgXOi@7FnRI)p}wF$8JyR{(6-@bJH;DY!KQIS`?wm*eJ#;xbSrzf-DrV(R^V28;bn zs~&Wp@SRy+-eUd3mErZbtF4kew9yRtmm>t(n3@ewv|+#UW6yYwHN?zWjo2NQYO_*X zFK@(>*8K_MkC<~6=BuH8;pe*{E#71l=Z1=Wjv621=VS;m>$<0w*AjC;I=yEhX@zK!rux z)@CEFv`MUB5G~_yj&X(T5|3M3yp000Tby+!2ny!8A^mM={S8SSJPGpg$^b>=$C&5- zED4Fw11&FYCPAm4J(#6f0FXp`pVm#w!9^|EA-rq^-WC^IeqJpuDTpzmb;HwH`YMR& zN2_;FLyfBLC*XZFD9&MWC_Ei%(Oze)5Eun*rcD#1dTS7Zb5sTqOW0S_ zynN?ux~0%AhL&=DG#id$Ke{Juu~dlQP8&TnjxE5Dq+RFLI;nU2n&RNpHQJ0skvTjC zJ__6vVHkbQ-TXp&IUlPOyfGE}(s)BY%U6|@+C~5+toFXYo?riaTgwO5h7YC}! z4yiaq>bdNOnG&xlO{!9LP3bBUjuY?lGk;(N9o!MU8pmh-6IQ6;46?j`BYcz#I(?W6!gz=aNvGSmU`KA{!U?NMF60F#B!`fH;Pi&1;Tl9JCPVZ~=?!t@l>8l!U*W$|msk4#R32lw&tDuFBHZNsP5#4}R&!}Xn~NhRPwEpN zP@|0*>WMVqWhpel(L)56AEfnZB*w^0^|m(xy;>j?5wE96 zn$bg5NdEZfBWC+LFf6LcLz(yum+Z zN6(zH{I`fBUm#RIU4T-q>wtoinU7Zh42OSFp!CdYWmJP7ruPmIfzs*OZcYz&Lea+_ z;6k*ty&$v8561BhK{*!uw5{Ng5uU)Xnn{?u-`se;%^-{UjH5Dm7}a8ca#Uh~KsHS= z3PD1qOG2d6flAo-sBqgC@7mgq;fElf1?n2S;XkaI%&~n}5s|I&vkC$tx@u<|!D3{w z3CtL_2`sWW!;|$0zO(g92a4o7Zl^1kaSBAgur*PbKHJ_9B!QB{Q0v2`&#Sc_hF1{h z)n@M3SI1g{=*7nC58}C|Vf8P4dt$v0DDcoOFYn~D`m;Vt>zGn6c0cLfqnFtI^Qkf_ z{C<3uYY`&AB3Lj-24uF;fe7#;OnF_otP=rh1&{=k%Q&BLDBOF7w=RW;504Rd*bA|2 z_c<)a`-%QStS2thf$butCj_yYm)k`ZK?RD*Wf{qtToTCz6hH6iTFQy+T8;s<&Bv=W zwL5}+Vg=tOKn!HI9_<7U^KU~A$gc<w{(3A^0x%=;lw~?OFj@+ZzL6Ft>2D-z67Q4%|=%l z5m#(y+3J6lK0wzhFYruCA=H9L(61IIC&PsQ?D(1YRj`Z@K}^&68M>|FSdU+Xbf5nf zY9!>7EQ1O1Wwtk6#V}c$X5J%HrGdR2yPhd}R~!O@5E{o5ew&cyEnoiv#{LHXWqEXn z1v4n?6&$^0;i|tp??~k7qkL|Whw=^(p>Ox7S!xZU-_=yYK z`g-EC996(iZvup2E=;hM3lofSQC>d84@!tv+3`fF@%-bsvoFw_d--R`RczDU52mi6 z5avzm#SBwfzG#gcYLfpZnTJ762;7Uu74D}& zp;q08sM2sEJNC?RDjCKW2d?G){43^h2_X-bM1;dEs8>qU;jR&jo5d*C>AG_FXvEd#J zd_bDiJGae4o9+@N+A@3s8dCQJ+qv2|WauY)c{+rRdW24Sz*o==A(XRf?QvLTwt0Zj zA^lb*eTw%gB^;32-iWW(8QGz#K z^%7KTE9^xt(gRmj*lS*_*;HsCM&X?&MzVLjl+~f!)>XaZMY+A;MG~V|{(2Zdg}vRS zti_rR_G%a6(wksU0NUH@sSqQNml)wO_^ec2*ibu)lo~2}x2uOEAbMI9BaE&yf_z8Y zk~^xLvn`0r!ILSoiSk+{DW(6ymUdeS3HJFUN|^s5`CBG_%w_&Ziv<_&HXfXe3zDqAJUSV zkp;-&+-spvatzApJYk9#qRaKB|2X<9=ph^64tX_c}DXmQkhEGt>#oY@Mkyj{M} z1xu|ti;w)0Oz*((We$$GY4iv*oOMJ|Y&~A5bu$|PAgvP_P|}qxH!+68dUR#^DsUvc zqZ(>xXE2KlM|~Zq$O^KLR$4e?(hQ`cnX-2zBeZ_&!(2rkuh>WX85h^WIVQqLGm-56 ztWFQJ`KGiYXj(Rwv$mc!%#PgRq=am&EQMub!BTJX30c_3g={QTkI5oHi-ZV)ghv&P zXCNa*aMFjHxd0_cW|e}?xBxm^VOoJ-%7xRqI^c9ye+#cnn=J>5Cm&GaSI>}>d`s>VOj zd7v6*Sp|F+^?a}7fhFe87FP5n&TR2SuVU}yvAu`xK639p`+Bf0pITh%-pJ=>&m5Zy zA5=ZoT|72Bb8K2)NYo9FMa_+~`tT+*>I1i{;J+oialW%~d}@ZtmZr{UJn(Jfb5l#2 ze3=hGmP0HRAA$tBV<+v?p!(z^(=1OJ?nPnopf4_}W$_ivrDbcp#0Mg+?=e1ic`P(7 zI@otCZ8&v1t}l`vkzfaat}3Jv$>}2JB~N7K^WauBO@I!u_5)JUHC}sQKA5v)!4MvwRSmkDIQ-tq>s?y?C~JjBhQU>?|zOeSZG<)b#0|c0MrM zJ(YD&O?9({sWYgxFj~%L7qaQ%8qQZe__K|6@oux%EPRGw@ zv)$#+{E69xWxjzvJ3Y&{!?Tk>gf<$v3UMgSnPDuF(&m`azq@F9Tv(Nok)?@P=sa*N zAY;%M`dikP=5tv#mAdYfjKs2i-MYJYy0fsnJYBZ^;v^@0O17X6FqJZdDTgYM3IflZ z4}9jMwLz}$qAo~%0e;!7;7!)y^YGJ^q=&XSRPPoa@n*cvbarNCxtksDXsYh1PPQz8 zTGn^m89!iSRYjjK3ZLRj%_VpRUaLzpli{#-NyV^zNaBfEELds&Sk)F#m}Simfa-vV z3rN!p38x;W&P~nEPaU77npi+Wgct744MXT3X1Prm^r&Ri|jD~on)z`?R+Yq)(oR>gxi9ZVcZ0-0bbId v&;71+imy6nCqSYzbLN@9Z(g)GJGC%7oh?pJuPiNfX7B>u)M%z}Bh&KFMDYS4 literal 0 HcmV?d00001 From 38ec8d780abc6b92d34c7b2f2e9386984d6359b8 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 10 Jul 2024 18:32:18 +0300 Subject: [PATCH 44/82] add tests --- .../tests/test_cli_contracts.py | 45 ++++++++++++++++++- .../call_multisig_propose_batch_args.json | 20 +++++++++ .../tests/testdata/upgrade_multisig_args.json | 15 +++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 multiversx_sdk_cli/tests/testdata/call_multisig_propose_batch_args.json create mode 100644 multiversx_sdk_cli/tests/testdata/upgrade_multisig_args.json diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index 94ec42be..a19f3453 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -350,8 +350,49 @@ def test_contract_deploy_with_abi(capsys: Any): 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") + + +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" + ]) + assert not return_code + + data = get_transaction_data(capsys) + 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" + ]) + assert not return_code + + data = get_transaction_data(capsys) + assert data == "proposeSCUpgradeFromSource@000000000000000005000a14b9cb3f346116ded6802f2eb0235a36b2997569e1@@00000000000000000500ed8e25a94efa837aae0e593112cfbb01b448755069e1@0500@" def _read_stdout(capsys: Any) -> str: @@ -369,7 +410,7 @@ def get_query_response(capsys: Any): return json.loads(out)[0] -def get_transaction_data(capsys: Any): +def get_transaction_data(capsys: Any) -> str: out = _read_stdout(capsys) output = json.loads(out) return output["emittedTransactionData"] diff --git a/multiversx_sdk_cli/tests/testdata/call_multisig_propose_batch_args.json b/multiversx_sdk_cli/tests/testdata/call_multisig_propose_batch_args.json new file mode 100644 index 00000000..02a1a89a --- /dev/null +++ b/multiversx_sdk_cli/tests/testdata/call_multisig_propose_batch_args.json @@ -0,0 +1,20 @@ +[ + [ + { + "__discriminant__": 5, + "0": { + "to": { + "bech32": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3" + }, + "egld_amount": 1000000000000000000, + "endpoint_name": "add", + "arguments": [ + { + "hex": "07" + } + ], + "opt_gas_limit": 15000000 + } + } + ] +] diff --git a/multiversx_sdk_cli/tests/testdata/upgrade_multisig_args.json b/multiversx_sdk_cli/tests/testdata/upgrade_multisig_args.json new file mode 100644 index 00000000..33731ca0 --- /dev/null +++ b/multiversx_sdk_cli/tests/testdata/upgrade_multisig_args.json @@ -0,0 +1,15 @@ +[ + { + "bech32": "erd1qqqqqqqqqqqqqpgqpg2tnjelx3s3dhkksqhjavprtgmt9xt4d8ss9al8m4" + }, + 0, + { + "bech32": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3" + }, + { + "hex": "0500" + }, + [ + 0 + ] +] From dd1442deeec7930f2133d86ebbd6a14eda93c50b Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 11 Jul 2024 15:33:40 +0300 Subject: [PATCH 45/82] add suport for contract queries using abi file --- multiversx_sdk_cli/cli_contracts.py | 20 ++++- multiversx_sdk_cli/cli_dns.py | 18 +++- multiversx_sdk_cli/contracts.py | 130 ++++++++++++---------------- multiversx_sdk_cli/dns.py | 81 ++++++++++++++--- 4 files changed, 155 insertions(+), 94 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 88460db0..ceec4380 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -13,7 +13,7 @@ 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, query_contract +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 @@ -132,6 +132,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: 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) _add_function_arg(sub) _add_arguments_arg(sub) @@ -443,17 +444,28 @@ def upgrade(args: Any): def query(args: Any): logger.debug("query") - # workaround so we can use the function bellow + # workaround so we can use the function bellow to set chainID args.chain = "" cli_shared.prepare_chain_id_in_args(args) + config = TransactionsFactoryConfig(args.chain) + abi = Abi.load(Path(args.abi)) if args.abi else None + contract = SmartContract(config, abi) + + arguments, args_from_file = _get_contract_arguments(args) contract_address = Address.new_from_bech32(args.contract) proxy = ProxyNetworkProvider(args.proxy) function = args.function - arguments: List[Any] = args.arguments or [] - result = query_contract(contract_address, proxy, function, arguments) + result = contract.query_contract( + contract_address=contract_address, + proxy=proxy, + function=function, + arguments=arguments, + args_from_file=args_from_file + ) + utils.dump_out_json(result) diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index a7c8dcd6..1e87358e 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -9,6 +9,7 @@ dns_address_for_name, name_hash, register, registration_cost, resolve, validate_name, version) +from multiversx_sdk_cli.errors import ArgumentsNotProvidedError def setup_parser(args: List[str], subparsers: Any) -> Any: @@ -32,7 +33,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: 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", 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) @@ -41,12 +42,12 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: 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", 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=get_registration_cost) sub = cli_shared.add_command_subparser(subparsers, "dns", "version", "Asks the contract for its version") - sub.add_argument("--shard-id", 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)") 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) @@ -70,13 +71,21 @@ def _add_name_arg(sub: Any): sub.add_argument("name", help="the name for which to check") +def _ensure_proxy_is_provided(args: Any): + if not args.proxy: + raise ArgumentsNotProvidedError("'--proxy' argument not provided") + + def dns_resolve(args: Any): + _ensure_proxy_is_provided(args) + addr = resolve(args.name, ProxyNetworkProvider(args.proxy)) if addr.to_hex() != Address.new_from_bech32(ADDRESS_ZERO_BECH32).to_hex(): print(addr.to_bech32()) def dns_validate_name(args: Any): + _ensure_proxy_is_provided(args) validate_name(args.name, args.shard_id, ProxyNetworkProvider(args.proxy)) @@ -97,10 +106,13 @@ def get_dns_address_for_name_hex(args: Any): def get_registration_cost(args: Any): + _ensure_proxy_is_provided(args) print(registration_cost(args.shard_id, ProxyNetworkProvider(args.proxy))) def get_version(args: Any): + _ensure_proxy_is_provided(args) + proxy = ProxyNetworkProvider(args.proxy) if args.all: t = PrettyTable(['Shard ID', 'Contract address (bech32)', 'Contract address (hex)', 'Version']) diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index e6dcafa8..555df329 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -1,9 +1,11 @@ -import base64 import logging from pathlib import Path -from typing import Any, List, Optional, Protocol, Sequence, Union +from typing import Any, Dict, List, Optional, Protocol, Sequence, Union -from multiversx_sdk import (Address, SmartContractTransactionsFactory, Token, +from multiversx_sdk import (Address, QueryRunnerAdapter, + SmartContractQueriesController, + SmartContractQueryResponse, + SmartContractTransactionsFactory, Token, TokenComputer, TokenTransfer, Transaction, TransactionPayload) from multiversx_sdk.abi import Abi @@ -63,6 +65,10 @@ class IContractQueryResponse(Protocol): return_data: List[str] return_code: str return_message: str + gas_used: int + + def get_return_data_parts(self) -> List[bytes]: + ... class IConfig(Protocol): @@ -95,7 +101,7 @@ def prepare_deploy_transaction(self, guardian: str) -> Transaction: args = arguments if arguments else [] if not args_from_file: - args = self.prepare_args_for_factory(args) + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_deploy( sender=owner.address, @@ -133,7 +139,7 @@ def prepare_execute_transaction(self, args = arguments if arguments else [] if not args_from_file: - args = self.prepare_args_for_factory(args) + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_execute( sender=caller.address, @@ -168,11 +174,9 @@ def prepare_upgrade_transaction(self, version: int, options: int, guardian: str) -> Transaction: - args = self.prepare_args_for_factory(arguments) if arguments else [] - args = arguments if arguments else [] if not args_from_file: - args = self.prepare_args_for_factory(args) + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_upgrade( sender=owner.address, @@ -194,6 +198,40 @@ def prepare_upgrade_transaction(self, return tx + def query_contract(self, + contract_address: IAddress, + proxy: INetworkProvider, + function: str, + arguments: List[Any], + args_from_file: bool,) -> List[Any]: + args = arguments if arguments else [] + if not args_from_file: + args = self._prepare_args_for_factory(args) + + query_runner = QueryRunnerAdapter(proxy) + sc_query_controller = SmartContractQueriesController(query_runner, self._abi) + + query = sc_query_controller.create_query( + contract=contract_address.to_bech32(), + function=function, + arguments=args + ) + + response = sc_query_controller.run_query(query) + + if self._abi: + return sc_query_controller.parse_query_response(response) + else: + return [self._query_response_to_dict(response)] + + def _query_response_to_dict(self, response: SmartContractQueryResponse) -> Dict[str, Any]: + return { + "function": response.function, + "returnCode": response.return_code, + "returnMessage": response.return_message, + "returnDataParts": [part.hex() for part in response.return_data_parts] + } + def _prepare_token_transfers(self, transfers: List[str]) -> List[TokenTransfer]: token_computer = TokenComputer() token_transfers: List[TokenTransfer] = [] @@ -209,12 +247,12 @@ def _prepare_token_transfers(self, transfers: List[str]) -> List[TokenTransfer]: return token_transfers - def prepare_args_for_factory(self, arguments: List[str]) -> 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): - args.append(hex_to_bytes(arg)) + args.append(self._hex_to_bytes(arg)) elif arg.isnumeric(): args.append(int(arg)) elif arg.startswith(DEFAULT_HRP): @@ -230,64 +268,11 @@ def prepare_args_for_factory(self, arguments: List[str]) -> List[Any]: return args - -def query_contract( - contract_address: IAddress, - proxy: INetworkProvider, - function: str, - arguments: List[Any], - value: int = 0, - caller: Optional[IAddress] = None -) -> List[Any]: - response_data = query_detailed(contract_address, proxy, function, arguments, value, caller) - return_data = response_data.return_data - return [_interpret_return_data(data) for data in return_data] - - -def query_detailed(contract_address: IAddress, proxy: INetworkProvider, function: str, arguments: List[Any], - value: int = 0, caller: Optional[IAddress] = None) -> Any: - arguments = arguments or [] - # Temporary workaround, until we use sdk-core's serializer. - arguments_hex = [_prepare_argument(arg) for arg in arguments] - prepared_arguments_bytes = [bytes.fromhex(arg) for arg in arguments_hex] - - query = ContractQuery(contract_address, function, value, prepared_arguments_bytes, caller) - - response = proxy.query_contract(query) - # Temporary workaround, until we add "isSuccess" on the response class. - if response.return_code != "ok": - raise RuntimeError(f"Query failed: {response.return_message}") - return response - - -def _interpret_return_data(data: str) -> Any: - if not data: - return data - - try: - as_bytes = base64.b64decode(data) - as_hex = as_bytes.hex() - as_number = _interpret_as_number_if_safely(as_hex) - - result = QueryResult(data, as_hex, as_number) - return result - except Exception: - logger.warn(f"Cannot interpret return data: {data}") - return None - - -def _interpret_as_number_if_safely(as_hex: str) -> Optional[int]: - """ - Makes sure the string can be safely converted to an int (and then back to a string). - - See: - - https://stackoverflow.com/questions/73693104/valueerror-exceeds-the-limit-4300-for-integer-string-conversion - - https://github.com/python/cpython/issues/95778 - """ - try: - return int(str(int(as_hex or "0", 16))) - except: - return None + def _hex_to_bytes(self, arg: str): + argument = arg[len(HEX_PREFIX):] + argument = argument.upper() + argument = ensure_even_length(argument) + return bytes.fromhex(argument) def prepare_execute_transaction_data(function: str, arguments: List[Any]) -> TransactionPayload: @@ -299,14 +284,7 @@ def prepare_execute_transaction_data(function: str, arguments: List[Any]) -> Tra return TransactionPayload.from_str(tx_data) -def hex_to_bytes(arg: str): - argument = arg[len(HEX_PREFIX):] - argument = argument.upper() - argument = ensure_even_length(argument) - return bytes.fromhex(argument) - - -# only used for contract queries and stake operations +# only used for stake operations def _prepare_argument(argument: Any): as_str = str(argument) as_hex = _to_hex(as_str) diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 26fce0b9..5859a4ef 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -1,12 +1,13 @@ from typing import Any, List, Protocol from Cryptodome.Hash import keccak -from multiversx_sdk import Address, AddressComputer +from multiversx_sdk import Address, AddressComputer, TransactionsFactoryConfig +from multiversx_sdk.network_providers.network_config import NetworkConfig from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.constants import ADDRESS_ZERO_BECH32, DEFAULT_HRP -from multiversx_sdk_cli.contracts import query_contract +from multiversx_sdk_cli.contracts import SmartContract from multiversx_sdk_cli.transactions import (compute_relayed_v1_data, do_prepare_transaction) @@ -19,22 +20,56 @@ class INetworkProvider(Protocol): def query_contract(self, query: Any) -> Any: ... + def get_network_config(self) -> NetworkConfig: + ... + def resolve(name: str, proxy: INetworkProvider) -> Address: name_arg = "0x{}".format(str.encode(name).hex()) dns_address = dns_address_for_name(name) - result = query_contract(dns_address, proxy, "resolve", [name_arg]) - if len(result) == 0: + chain_id = proxy.get_network_config().chain_id + config = TransactionsFactoryConfig(chain_id) + contract = SmartContract(config) + + response = contract.query_contract( + contract_address=dns_address, + proxy=proxy, + function="resolve", + arguments=[name_arg], + args_from_file=False + ) + + if len(response) == 0: return Address.from_bech32(ADDRESS_ZERO_BECH32) - return Address.from_hex(result[0].hex, DEFAULT_HRP) + + result = response[0].get("returnDataParts")[0] + return Address.from_hex(result, DEFAULT_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) - query_contract(dns_address, proxy, "validateName", [name_arg]) + chain_id = proxy.get_network_config().chain_id + config = TransactionsFactoryConfig(chain_id) + contract = SmartContract(config) + + response = contract.query_contract( + contract_address=dns_address, + proxy=proxy, + function="validateName", + arguments=[name_arg], + args_from_file=False + )[0] + + return_code = response["returnCode"] + if return_code == "ok": + print(f"name [{name}] is valid") + else: + print(f"name [{name}] is invalid") + + print(response) def register(args: Any): @@ -69,17 +104,41 @@ def name_hash(name: str) -> bytes: def registration_cost(shard_id: int, proxy: INetworkProvider) -> int: dns_address = compute_dns_address_for_shard_id(shard_id) - result = query_contract(dns_address, proxy, "getRegistrationCost", []) - if len(result[0]) == 0: + + chain_id = proxy.get_network_config().chain_id + config = TransactionsFactoryConfig(chain_id) + contract = SmartContract(config) + + response = contract.query_contract( + contract_address=dns_address, + proxy=proxy, + function="getRegistrationCost", + arguments=[], + args_from_file=False + )[0] + + data = response["returnDataParts"][0] + if not data: return 0 else: - return int("0x{}".format(result[0])) + return int("0x{}".format(data)) def version(shard_id: int, proxy: INetworkProvider) -> str: dns_address = compute_dns_address_for_shard_id(shard_id) - result = query_contract(dns_address, proxy, "version", []) - return bytearray.fromhex(result[0].hex).decode() + + chain_id = proxy.get_network_config().chain_id + config = TransactionsFactoryConfig(chain_id) + contract = SmartContract(config) + + response = contract.query_contract( + contract_address=dns_address, + proxy=proxy, + function="version", + arguments=[], + args_from_file=False + )[0] + return bytearray.fromhex(response["returnDataParts"][0]).decode() def dns_address_for_name(name: str) -> Address: From 73fa3e4d1b466b3e9ecdc8dfd84adbd7b3ea893f Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 11 Jul 2024 15:48:02 +0300 Subject: [PATCH 46/82] remove args preparation for upgrading contract --- multiversx_sdk_cli/contracts.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index e6dcafa8..820ca009 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -168,8 +168,6 @@ def prepare_upgrade_transaction(self, version: int, options: int, guardian: str) -> Transaction: - args = self.prepare_args_for_factory(arguments) if arguments else [] - args = arguments if arguments else [] if not args_from_file: args = self.prepare_args_for_factory(args) From 7057b2d7c46b2289bd6d69f4f4927dc2eb830986 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 17 Jul 2024 11:03:03 +0300 Subject: [PATCH 47/82] fixes after review --- multiversx_sdk_cli/cli_contracts.py | 23 +++++++++++-------- multiversx_sdk_cli/contracts.py | 16 ++++++------- .../tests/test_cli_contracts.py | 6 ++--- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 88460db0..cb8ea41e 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -242,7 +242,7 @@ def _add_arguments_arg(sub: Any): 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 }") + "E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }]") def _add_token_transfers_args(sub: Any): @@ -328,13 +328,13 @@ def deploy(args: Any): abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) - arguments, args_from_file = _get_contract_arguments(args) + arguments, should_prepare_args = _get_contract_arguments(args) tx = contract.prepare_deploy_transaction( owner=sender, bytecode=Path(args.bytecode), arguments=arguments, - args_from_file=args_from_file, + should_prepare_args=should_prepare_args, upgradeable=args.metadata_upgradeable, readable=args.metadata_readable, payable=args.metadata_payable, @@ -383,7 +383,7 @@ def call(args: Any): abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) - arguments, args_from_file = _get_contract_arguments(args) + arguments, should_prepare_args = _get_contract_arguments(args) contract_address = Address.new_from_bech32(args.contract) tx = contract.prepare_execute_transaction( @@ -391,7 +391,7 @@ def call(args: Any): contract=contract_address, function=args.function, arguments=arguments, - args_from_file=args_from_file, + should_prepare_args=should_prepare_args, gas_limit=int(args.gas_limit), value=int(args.value), transfers=args.token_transfers, @@ -416,7 +416,7 @@ def upgrade(args: Any): abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) - arguments, args_from_file = _get_contract_arguments(args) + arguments, should_prepare_args = _get_contract_arguments(args) contract_address = Address.new_from_bech32(args.contract) tx = contract.prepare_upgrade_transaction( @@ -424,7 +424,7 @@ def upgrade(args: Any): contract=contract_address, bytecode=Path(args.bytecode), arguments=arguments, - args_from_file=args_from_file, + should_prepare_args=should_prepare_args, upgradeable=args.metadata_upgradeable, readable=args.metadata_readable, payable=args.metadata_payable, @@ -461,12 +461,15 @@ 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: - raise Exception("Both '--arguments' and '--arguments-file' provided.") + raise Exception("Provide either '--arguments' or '--arguments-file'.") if json_args: - return json_args, True + if not args.abi: + raise Exception("Can't use '--arguments-file' without providing the Abi file.") + + return json_args, False else: - return args.arguments, False + return args.arguments, True def _send_or_simulate(tx: Transaction, contract_address: IAddress, args: Any): diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 820ca009..5ebd8fe2 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -82,7 +82,7 @@ def prepare_deploy_transaction(self, owner: Account, bytecode: Path, arguments: Union[List[Any], None], - args_from_file: bool, + should_prepare_args: bool, upgradeable: bool, readable: bool, payable: bool, @@ -94,7 +94,7 @@ def prepare_deploy_transaction(self, options: int, guardian: str) -> Transaction: args = arguments if arguments else [] - if not args_from_file: + if should_prepare_args: args = self.prepare_args_for_factory(args) tx = self._factory.create_transaction_for_deploy( @@ -121,7 +121,7 @@ def prepare_execute_transaction(self, contract: Address, function: str, arguments: Union[List[Any], None], - args_from_file: bool, + should_prepare_args: bool, gas_limit: int, value: int, transfers: Union[List[str], None], @@ -132,7 +132,7 @@ def prepare_execute_transaction(self, token_transfers = self._prepare_token_transfers(transfers) if transfers else [] args = arguments if arguments else [] - if not args_from_file: + if should_prepare_args: args = self.prepare_args_for_factory(args) tx = self._factory.create_transaction_for_execute( @@ -157,7 +157,7 @@ def prepare_upgrade_transaction(self, contract: IAddress, bytecode: Path, arguments: Union[List[str], None], - args_from_file: bool, + should_prepare_args: bool, upgradeable: bool, readable: bool, payable: bool, @@ -169,7 +169,7 @@ def prepare_upgrade_transaction(self, options: int, guardian: str) -> Transaction: args = arguments if arguments else [] - if not args_from_file: + if should_prepare_args: args = self.prepare_args_for_factory(args) tx = self._factory.create_transaction_for_upgrade( @@ -279,12 +279,12 @@ def _interpret_as_number_if_safely(as_hex: str) -> Optional[int]: Makes sure the string can be safely converted to an int (and then back to a string). See: - - https://stackoverflow.com/questions/73693104/valueerror-exceeds-the-limit-4300-for-integer-string-conversion + - https://stackoverflow.com/questions/73693104/valueerror-exceeds-the-limit-4300-for-integer-string-conversion - https://github.com/python/cpython/issues/95778 """ try: return int(str(int(as_hex or "0", 16))) - except: + except Exception: return None diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index a19f3453..acaea1da 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -107,7 +107,7 @@ def test_contract_deploy(): str(output_file), ] ) - assert Path.is_file(output_file) == True + assert Path.is_file(output_file) def test_contract_upgrade(): @@ -136,7 +136,7 @@ def test_contract_upgrade(): str(output_file), ] ) - assert Path.is_file(output_file) == True + assert Path.is_file(output_file) def test_contract_call(): @@ -165,7 +165,7 @@ def test_contract_call(): str(output_file), ] ) - assert Path.is_file(output_file) == True + assert Path.is_file(output_file) def test_contract_transfer_and_execute(capsys: Any): From 7aaeb0a5e58abbd881919be879972f6a30dc8f50 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 17 Jul 2024 13:04:52 +0300 Subject: [PATCH 48/82] generate wallet in specific shard --- multiversx_sdk_cli/cli_wallet.py | 24 ++++++++++++++++++--- multiversx_sdk_cli/tests/test_cli_wallet.py | 24 ++++++++++++++++++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 9a71f8d1..75177e42 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -7,9 +7,10 @@ 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.constants import DEFAULT_HRP +from multiversx_sdk_cli.constants import DEFAULT_HRP, NUMBER_OF_SHARDS from multiversx_sdk_cli.errors import KnownError from multiversx_sdk_cli.sign_verify import SignedMessage, sign_message from multiversx_sdk_cli.ux import show_critical_error, show_message @@ -32,6 +33,9 @@ WALLET_FORMATS_AND_ADDRESSES = [*WALLET_FORMATS, WALLET_FORMAT_ADDRESS_BECH32, WALLET_FORMAT_ADDRESS_HEX] +MAX_ITERATIONS_FOR_GENERATING_WALLET = 100 +CURRENT_SHARDS = [0, 1, 2] + def setup_parser(args: List[str], subparsers: Any) -> Any: parser = cli_shared.add_group_subparser( @@ -50,6 +54,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: 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=DEFAULT_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( @@ -107,8 +112,21 @@ def wallet_new(args: Any): format = args.format outfile = args.outfile address_hrp = args.address_hrp + shard = args.shard + + if shard in CURRENT_SHARDS: + i = 0 + while i < MAX_ITERATIONS_FOR_GENERATING_WALLET: + mnemonic = Mnemonic.generate() + pubkey = mnemonic.derive_key().generate_public_key() + generated_address_shard = get_shard_of_pubkey(pubkey.buffer, NUMBER_OF_SHARDS) + + if shard == generated_address_shard: + break + raise Exception(f"Couldn't generate wallet in shard {shard}") + else: + mnemonic = Mnemonic.generate() - mnemonic = Mnemonic.generate() print(f"Mnemonic: {mnemonic.get_text()}") print(f"Wallet address: {mnemonic.derive_key().generate_public_key().to_address(address_hrp).to_bech32()}") @@ -158,7 +176,7 @@ def convert_wallet(args: Any): if infile: input_text = infile.read_text() else: - print(f"Insert text below. Press 'Ctrl-D' (Linux / MacOS) or 'Ctrl-Z' (Windows) when done.") + print("Insert text below. Press 'Ctrl-D' (Linux / MacOS) or 'Ctrl-Z' (Windows) when done.") input_text = sys.stdin.read().strip() mnemonic, secret_key = _load_wallet(input_text, in_format, address_index) diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index 3a8e0db6..6121dbcc 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -3,7 +3,8 @@ from pathlib import Path from typing import Any -from multiversx_sdk import Mnemonic, UserPEM, UserWallet +from multiversx_sdk import (Address, AddressComputer, Mnemonic, UserPEM, + UserWallet) from multiversx_sdk_cli.cli import main @@ -17,6 +18,22 @@ def test_wallet_new(capsys: Any): assert Mnemonic.is_text_valid(displayed_mnemonic) +def test_generate_wallet_in_specific_shard(capsys: Any): + address_computer = AddressComputer() + + main(["wallet", "new", "--shard", "0"]) + address = Address.new_from_bech32(_read_stdout_wallet_address(capsys)) + assert address_computer.get_shard_of_address(address) == 0 + + main(["wallet", "new", "--shard", "1"]) + address = Address.new_from_bech32(_read_stdout_wallet_address(capsys)) + assert address_computer.get_shard_of_address(address) == 1 + + main(["wallet", "new", "--shard", "2"]) + address = Address.new_from_bech32(_read_stdout_wallet_address(capsys)) + assert address_computer.get_shard_of_address(address) == 2 + + def test_wallet_new_and_save_in_pem_format(capsys: Any): outfile = testdata_out_path / "testWallet.pem" outfile.unlink(missing_ok=True) @@ -371,6 +388,11 @@ def _read_stdout_mnemonic(capsys: Any) -> str: return lines[0].replace("Mnemonic:", "").strip() +def _read_stdout_wallet_address(capsys: Any) -> str: + lines = _read_stdout(capsys).split("\n") + return lines[1].replace("Wallet address:", "").strip() + + def _read_stdout(capsys: Any) -> str: return capsys.readouterr().out.strip() From fdc98089444574098c9d1a545999c352376070aa Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 17 Jul 2024 13:44:14 +0300 Subject: [PATCH 49/82] fix while loop when generating wallet in specific shard --- multiversx_sdk_cli/cli_wallet.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 75177e42..f924fddd 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -123,7 +123,10 @@ def wallet_new(args: Any): if shard == generated_address_shard: break - raise Exception(f"Couldn't generate wallet in shard {shard}") + + i += 1 + if i == 100: + raise Exception(f"Couldn't generate wallet in shard {shard}") else: mnemonic = Mnemonic.generate() From 39f5a2b07e3ad294ace899363e31ba13ea1b82b1 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 17 Jul 2024 14:12:18 +0300 Subject: [PATCH 50/82] fixes after review --- multiversx_sdk_cli/cli_wallet.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index f924fddd..ac6a0ee0 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -34,7 +34,7 @@ WALLET_FORMATS_AND_ADDRESSES = [*WALLET_FORMATS, WALLET_FORMAT_ADDRESS_BECH32, WALLET_FORMAT_ADDRESS_HEX] MAX_ITERATIONS_FOR_GENERATING_WALLET = 100 -CURRENT_SHARDS = [0, 1, 2] +CURRENT_SHARDS = [i for i in range(NUMBER_OF_SHARDS)] def setup_parser(args: List[str], subparsers: Any) -> Any: @@ -115,8 +115,7 @@ def wallet_new(args: Any): shard = args.shard if shard in CURRENT_SHARDS: - i = 0 - while i < MAX_ITERATIONS_FOR_GENERATING_WALLET: + for i in range(MAX_ITERATIONS_FOR_GENERATING_WALLET): mnemonic = Mnemonic.generate() pubkey = mnemonic.derive_key().generate_public_key() generated_address_shard = get_shard_of_pubkey(pubkey.buffer, NUMBER_OF_SHARDS) @@ -124,8 +123,7 @@ def wallet_new(args: Any): if shard == generated_address_shard: break - i += 1 - if i == 100: + if i == 99: raise Exception(f"Couldn't generate wallet in shard {shard}") else: mnemonic = Mnemonic.generate() From e05c3f80142a3d15c2b62a001f1f712900e2542f Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 17 Jul 2024 14:32:54 +0300 Subject: [PATCH 51/82] fixes after review --- multiversx_sdk_cli/cli_wallet.py | 16 +++++++++++----- multiversx_sdk_cli/errors.py | 5 +++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index ac6a0ee0..c789b860 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -11,7 +11,8 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.constants import DEFAULT_HRP, NUMBER_OF_SHARDS -from multiversx_sdk_cli.errors import KnownError +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 @@ -114,17 +115,22 @@ def wallet_new(args: Any): address_hrp = args.address_hrp shard = args.shard - if shard in CURRENT_SHARDS: - for i in range(MAX_ITERATIONS_FOR_GENERATING_WALLET): + if shard: + if shard not in CURRENT_SHARDS: + raise BadUserInput(f"Wrong shard provided. Choose between {CURRENT_SHARDS}") + + is_wallet_generated = False + for _ in range(MAX_ITERATIONS_FOR_GENERATING_WALLET): mnemonic = Mnemonic.generate() pubkey = mnemonic.derive_key().generate_public_key() generated_address_shard = get_shard_of_pubkey(pubkey.buffer, NUMBER_OF_SHARDS) if shard == generated_address_shard: + is_wallet_generated = True break - if i == 99: - raise Exception(f"Couldn't generate wallet in shard {shard}") + if not is_wallet_generated: + raise WalletGenerationError(f"Couldn't generate wallet in shard {shard}") else: mnemonic = Mnemonic.generate() diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 13b06e87..8e4cf247 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -208,3 +208,8 @@ def __init__(self, message: str, url: str, data: str, code: str): "code": code } super().__init__(message, inner) + + +class WalletGenerationError(KnownError): + def __init__(self, message: str): + super().__init__(message) From f44e42b2702b71af97759994ff1529503a133a01 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 17 Jul 2024 14:45:53 +0300 Subject: [PATCH 52/82] small fix --- multiversx_sdk_cli/cli_wallet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index c789b860..f9433fd3 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -115,7 +115,7 @@ def wallet_new(args: Any): address_hrp = args.address_hrp shard = args.shard - if shard: + if shard is not None: if shard not in CURRENT_SHARDS: raise BadUserInput(f"Wrong shard provided. Choose between {CURRENT_SHARDS}") From 0e80e04283b0c6d7cb7ef08f538342742b560a8d Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 17 Jul 2024 15:30:38 +0300 Subject: [PATCH 53/82] renamings --- multiversx_sdk_cli/cli_contracts.py | 4 ++-- multiversx_sdk_cli/contracts.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index f0b09ab1..da4235ca 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -452,7 +452,7 @@ def query(args: Any): abi = Abi.load(Path(args.abi)) if args.abi else None contract = SmartContract(config, abi) - arguments, args_from_file = _get_contract_arguments(args) + arguments, should_prepare_args = _get_contract_arguments(args) contract_address = Address.new_from_bech32(args.contract) proxy = ProxyNetworkProvider(args.proxy) @@ -463,7 +463,7 @@ def query(args: Any): proxy=proxy, function=function, arguments=arguments, - args_from_file=args_from_file + should_prepare_args=should_prepare_args ) utils.dump_out_json(result) diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index e85aeaa0..d178dafb 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -203,9 +203,9 @@ def query_contract(self, proxy: INetworkProvider, function: str, arguments: List[Any], - args_from_file: bool,) -> List[Any]: + should_prepare_args: bool) -> List[Any]: args = arguments if arguments else [] - if not args_from_file: + if should_prepare_args: args = self._prepare_args_for_factory(args) query_runner = QueryRunnerAdapter(proxy) From 30b76068c02314a64fd0a893f46cda90495994ad Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 17 Jul 2024 16:11:33 +0300 Subject: [PATCH 54/82] remove test --- multiversx_sdk_cli/tests/test_contracts.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/multiversx_sdk_cli/tests/test_contracts.py b/multiversx_sdk_cli/tests/test_contracts.py index d44bb3f9..38f214ce 100644 --- a/multiversx_sdk_cli/tests/test_contracts.py +++ b/multiversx_sdk_cli/tests/test_contracts.py @@ -8,9 +8,7 @@ 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, - _interpret_as_number_if_safely, - _prepare_argument) +from multiversx_sdk_cli.contracts import SmartContract, _prepare_argument logging.basicConfig(level=logging.INFO) @@ -63,12 +61,6 @@ def test_contract_verification_create_request_signature(): assert signature.hex() == "30111258cc42ea08e0c6a3e053cc7086a88d614b8b119a244904e9a19896c73295b2fe5c520a1cb07cfe20f687deef9f294a0a05071e85c78a70a448ea5f0605" -def test_interpret_as_number_if_safely(): - assert _interpret_as_number_if_safely("") == 0 - assert _interpret_as_number_if_safely("0x5") == 5 - assert _interpret_as_number_if_safely("FF") == 255 - - def test_prepare_args_for_factories(): sc = SmartContract(TransactionsFactoryConfig("mock")) args = [ @@ -77,10 +69,10 @@ def test_prepare_args_for_factories(): "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" ] - arguments = sc.prepare_args_for_factory(args) + arguments = sc._prepare_args_for_factory(args) assert arguments[0] == b"\x05" assert arguments[1] == 123 - assert arguments[2] == False - assert arguments[3] == True + assert arguments[2] is False + assert arguments[3] is True assert arguments[4] == "test-string" assert arguments[5].to_bech32() == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" From 140a871abb0b4862f9acfa00d8790532121b77ff Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 17 Jul 2024 17:09:57 +0300 Subject: [PATCH 55/82] fixes --- multiversx_sdk_cli/cli_wallet.py | 5 ++++- multiversx_sdk_cli/contracts.py | 6 +++--- multiversx_sdk_cli/tests/test_cli_contracts.py | 8 ++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index f9433fd3..1ccbe199 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -3,7 +3,7 @@ import logging import sys from pathlib import Path -from typing import Any, List, Optional, Tuple +from typing import Any, List, Optional, Tuple, cast from multiversx_sdk import (Address, Mnemonic, UserPEM, UserSecretKey, UserWallet) @@ -134,6 +134,9 @@ def wallet_new(args: Any): else: mnemonic = Mnemonic.generate() + # this is done to get rid of the Pylance error: possibly unbound + mnemonic = cast(Mnemonic, mnemonic) # type: ignore + print(f"Mnemonic: {mnemonic.get_text()}") print(f"Wallet address: {mnemonic.derive_key().generate_public_key().to_address(address_hrp).to_bech32()}") diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index d178dafb..2b28ed29 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -101,7 +101,7 @@ def prepare_deploy_transaction(self, guardian: str) -> Transaction: args = arguments if arguments else [] if should_prepare_args: - args = self.prepare_args_for_factory(args) + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_deploy( sender=owner.address, @@ -139,7 +139,7 @@ def prepare_execute_transaction(self, args = arguments if arguments else [] if should_prepare_args: - args = self.prepare_args_for_factory(args) + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_execute( sender=caller.address, @@ -176,7 +176,7 @@ def prepare_upgrade_transaction(self, guardian: str) -> Transaction: args = arguments if arguments else [] if should_prepare_args: - args = self.prepare_args_for_factory(args) + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_upgrade( sender=owner.address, diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index acaea1da..bb89ed44 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -229,7 +229,9 @@ def test_contract_flow(capsys: Any): "--proxy", "https://testnet-api.multiversx.com" ]) response = get_query_response(capsys) - assert response == "" + return_data_parts = response["returnDataParts"] + assert len(return_data_parts) == 1 + assert return_data_parts == [""] # Clear the captured content capsys.readouterr() @@ -256,7 +258,9 @@ def test_contract_flow(capsys: Any): "--proxy", "https://testnet-api.multiversx.com" ]) response = get_query_response(capsys) - assert response["number"] == 7 + return_data_parts = response["returnDataParts"] + assert len(return_data_parts) == 1 + assert return_data_parts == ["07"] # Clear the captured content capsys.readouterr() From 6e7a967f9062f9f77432ecf146870322ad3c38b4 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 26 Jul 2024 14:45:56 +0300 Subject: [PATCH 56/82] fixes and test for contract query --- multiversx_sdk_cli/contract_verification.py | 4 +- multiversx_sdk_cli/contracts.py | 33 ++--- multiversx_sdk_cli/errors.py | 48 +------ .../tests/test_cli_contracts.py | 119 +++++++++++++++--- .../tests/testdata/adder.abi.json | 72 +++++++++++ multiversx_sdk_cli/transactions.py | 2 +- multiversx_sdk_cli/utils.py | 6 +- 7 files changed, 201 insertions(+), 83 deletions(-) create mode 100644 multiversx_sdk_cli/tests/testdata/adder.abi.json diff --git a/multiversx_sdk_cli/contract_verification.py b/multiversx_sdk_cli/contract_verification.py index 01e833bb..cbad2d3d 100644 --- a/multiversx_sdk_cli/contract_verification.py +++ b/multiversx_sdk_cli/contract_verification.py @@ -112,7 +112,7 @@ def _create_request_signature(account: Account, contract_address: Address, reque def query_status_with_task_id(url: str, task_id: str, interval: int = 10): - logger.info(f"Please wait while we verify your contract. This may take a while.") + logger.info("Please wait while we verify your contract. This may take a while.") old_status = "" while True: @@ -120,7 +120,7 @@ def query_status_with_task_id(url: str, task_id: str, interval: int = 10): status = response.get("status", "") if status == "finished": - logger.info(f"Verification finished!") + logger.info("Verification finished!") dump_out_json(response) break elif status != old_status: diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 2b28ed29..8fa20a38 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -1,10 +1,9 @@ import logging from pathlib import Path -from typing import Any, Dict, List, Optional, Protocol, Sequence, Union +from typing import Any, List, Optional, Protocol, Sequence, Union from multiversx_sdk import (Address, QueryRunnerAdapter, SmartContractQueriesController, - SmartContractQueryResponse, SmartContractTransactionsFactory, Token, TokenComputer, TokenTransfer, Transaction, TransactionPayload) @@ -202,7 +201,7 @@ def query_contract(self, contract_address: IAddress, proxy: INetworkProvider, function: str, - arguments: List[Any], + arguments: Union[List[Any], None], should_prepare_args: bool) -> List[Any]: args = arguments if arguments else [] if should_prepare_args: @@ -211,26 +210,16 @@ def query_contract(self, query_runner = QueryRunnerAdapter(proxy) sc_query_controller = SmartContractQueriesController(query_runner, self._abi) - query = sc_query_controller.create_query( - contract=contract_address.to_bech32(), - function=function, - arguments=args - ) - - response = sc_query_controller.run_query(query) - - if self._abi: - return sc_query_controller.parse_query_response(response) - else: - return [self._query_response_to_dict(response)] + try: + response = sc_query_controller.query( + contract=contract_address.to_bech32(), + function=function, + arguments=args + ) + except Exception as e: + raise errors.QueryContractError("Couldn't query contract: ", e) - def _query_response_to_dict(self, response: SmartContractQueryResponse) -> Dict[str, Any]: - return { - "function": response.function, - "returnCode": response.return_code, - "returnMessage": response.return_message, - "returnDataParts": [part.hex() for part in response.return_data_parts] - } + return response def _prepare_token_transfers(self, transfers: List[str]) -> List[TokenTransfer]: token_computer = TokenComputer() diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 8e4cf247..19dae17f 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -1,5 +1,3 @@ - -from pathlib import Path from typing import Any, List, Tuple, Union @@ -22,16 +20,6 @@ class ProgrammingError(KnownError): pass -class TemplateMissingError(KnownError): - def __init__(self, template: str): - super().__init__(f"Template missing: {template}") - - -class BadTemplateError(KnownError): - def __init__(self, directory: Path): - super().__init__(f"Bad template: {directory}") - - class DownloadError(KnownError): pass @@ -75,7 +63,7 @@ def __init__(self, directory: str): class BadFile(KnownError): - def __init__(self, filename: str, inner=None): + def __init__(self, filename: str, inner: Any = None): super().__init__(f"Bad file: {filename}.", inner) @@ -104,16 +92,6 @@ def __init__(self, input: str, message: str): super().__init__(f"Bad input [{input}]: {message}") -class BadAddressFormatError(KnownError): - def __init__(self, value: str): - super().__init__(f"Bad address [{value}].") - - -class EmptyAddressError(KnownError): - def __init__(self): - super().__init__("Address is empty.") - - class ExternalProcessError(KnownError): def __init__(self, command_line: str, message: str): super().__init__(f"""External process error: @@ -136,25 +114,6 @@ def __init__(self, name: str): super().__init__(f"This configuration name is protected: {name}.") -class UnsupportedConfigurationValue(KnownError): - pass - - -class UnknownDerivationFunction(KnownError): - def __init__(self): - super().__init__("Unknown key derivation function.") - - -class UnknownCipher(KnownError): - def __init__(self, name: str): - super().__init__(f"Unknown cipher: {name}.") - - -class GasLimitTooLarge(KnownError): - def __init__(self, current: int, limit: int): - super().__init__(f"The gas limit provided ({current}) exceeds the max gas limit of allowed for a transaction ({limit})") - - class BadUserInput(KnownError): def __init__(self, message: str): super().__init__(f"Bad user input: {message}.") @@ -213,3 +172,8 @@ def __init__(self, message: str, url: str, data: str, code: str): class WalletGenerationError(KnownError): def __init__(self, message: str): super().__init__(message) + + +class QueryContractError(KnownError): + def __init__(self, message: str, inner: Any = None): + super().__init__(message, str(inner)) diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index bb89ed44..12e4c523 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -223,22 +223,19 @@ def test_contract_flow(capsys: Any): capsys.readouterr() main([ - "contract", "query", - contract, + "contract", "query", contract, "--function", "getSum", "--proxy", "https://testnet-api.multiversx.com" ]) response = get_query_response(capsys) - return_data_parts = response["returnDataParts"] - assert len(return_data_parts) == 1 - assert return_data_parts == [""] + assert len(response) == 1 + assert response == [""] # Clear the captured content capsys.readouterr() main([ - "contract", "call", - contract, + "contract", "call", contract, "--pem", alice, "--function", "add", "--recall-nonce", @@ -252,22 +249,19 @@ def test_contract_flow(capsys: Any): capsys.readouterr() main([ - "contract", "query", - contract, + "contract", "query", contract, "--function", "getSum", "--proxy", "https://testnet-api.multiversx.com" ]) response = get_query_response(capsys) - return_data_parts = response["returnDataParts"] - assert len(return_data_parts) == 1 - assert return_data_parts == ["07"] + assert len(response) == 1 + assert response == ["07"] # Clear the captured content capsys.readouterr() main([ - "contract", "upgrade", - contract, + "contract", "upgrade", contract, "--bytecode", adder, "--pem", alice, "--recall-nonce", @@ -399,6 +393,101 @@ def test_contract_upgrade_with_abi(capsys: Any): assert data == "proposeSCUpgradeFromSource@000000000000000005000a14b9cb3f346116ded6802f2eb0235a36b2997569e1@@00000000000000000500ed8e25a94efa837aae0e593112cfbb01b448755069e1@0500@" +def test_contract_query(capsys: Any): + alice = f"{parent}/testdata/alice.pem" + 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" + ]) + 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" + ]) + 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" + ]) + assert return_code + output = _read_stdout(capsys) + if "invalid function (not found)" in output: + assert True + else: + assert False + + # Clear the captured content + 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" + ]) + assert return_code + output = _read_stdout(capsys) + if "endpoint 'getSummm' not found" in output: + assert True + else: + assert False + + # Clear the captured content + capsys.readouterr() + + # query contract, without abi + 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"] + + # Clear the captured content + capsys.readouterr() + + # query contract, without abi + 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] + + def _read_stdout(capsys: Any) -> str: return capsys.readouterr().out.strip() @@ -411,7 +500,7 @@ def get_contract_address(capsys: Any): def get_query_response(capsys: Any): out = _read_stdout(capsys).replace("\n", "").replace(" ", "") - return json.loads(out)[0] + return json.loads(out) def get_transaction_data(capsys: Any) -> str: diff --git a/multiversx_sdk_cli/tests/testdata/adder.abi.json b/multiversx_sdk_cli/tests/testdata/adder.abi.json new file mode 100644 index 00000000..56bbedc0 --- /dev/null +++ b/multiversx_sdk_cli/tests/testdata/adder.abi.json @@ -0,0 +1,72 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.79.0", + "commitHash": "129f3b9964af4d4a709d1383930ade12dfe7c081", + "commitDate": "2024-06-10", + "channel": "Stable", + "short": "rustc 1.79.0 (129f3b996 2024-06-10)" + }, + "contractCrate": { + "name": "adder", + "version": "0.0.0", + "gitVersion": "v0.45.2.1-reproducible-337-g5478c6e" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.51.1" + } + }, + "docs": [ + "One of the simplest smart contracts possible,", + "it holds a single variable in storage, which anyone can increment." + ], + "name": "Adder", + "constructor": { + "inputs": [ + { + "name": "initial_value", + "type": "BigUint" + } + ], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [ + { + "name": "initial_value", + "type": "BigUint" + } + ], + "outputs": [] + }, + "endpoints": [ + { + "name": "getSum", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "docs": [ + "Add desired amount to the storage variable." + ], + "name": "add", + "mutability": "mutable", + "inputs": [ + { + "name": "value", + "type": "BigUint" + } + ], + "outputs": [] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": {} +} diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 65f53cc8..9c08e748 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -212,7 +212,7 @@ def tx_to_dictionary_as_inner_for_relayed_V1(tx: Transaction) -> Dict[str, Any]: dictionary["sndUserName"] = base64.b64encode(tx.sender_username.encode()).decode() if tx.receiver_username: - dictionary[f"rcvUserName"] = base64.b64encode(tx.receiver_username.encode()).decode() + dictionary["rcvUserName"] = base64.b64encode(tx.receiver_username.encode()).decode() return dictionary diff --git a/multiversx_sdk_cli/utils.py b/multiversx_sdk_cli/utils.py index a21596d4..bc4d5b9c 100644 --- a/multiversx_sdk_cli/utils.py +++ b/multiversx_sdk_cli/utils.py @@ -37,9 +37,13 @@ def to_json(self): class BasicEncoder(json.JSONEncoder): - def default(self, o: Any): + def default(self, o: Any) -> Any: if isinstance(o, ISerializable): return o.to_dictionary() + if isinstance(o, bytes): + return o.hex() + if isinstance(o, list): + return [self.default(item) for item in o] # type: ignore return super().default(o) From 3413f8c093a8b60c55eb0801a5172b7dca3ecb83 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 29 Jul 2024 11:22:23 +0300 Subject: [PATCH 57/82] fixes after review --- multiversx_sdk_cli/cli_contracts.py | 2 +- multiversx_sdk_cli/cli_wallet.py | 42 +++++++++---------- multiversx_sdk_cli/contracts.py | 37 +---------------- multiversx_sdk_cli/dns.py | 64 ++++++++++++++--------------- multiversx_sdk_cli/utils.py | 2 - 5 files changed, 55 insertions(+), 92 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index da4235ca..0ca7149e 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -444,7 +444,7 @@ def upgrade(args: Any): def query(args: Any): logger.debug("query") - # workaround so we can use the function bellow to set chainID + # workaround so we can use the function below to set chainID args.chain = "" cli_shared.prepare_chain_id_in_args(args) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 1ccbe199..06205f68 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -3,7 +3,7 @@ import logging import sys from pathlib import Path -from typing import Any, List, Optional, Tuple, cast +from typing import Any, List, Optional, Tuple from multiversx_sdk import (Address, Mnemonic, UserPEM, UserSecretKey, UserWallet) @@ -115,27 +115,7 @@ def wallet_new(args: Any): address_hrp = args.address_hrp shard = args.shard - if shard is not None: - if shard not in CURRENT_SHARDS: - raise BadUserInput(f"Wrong shard provided. Choose between {CURRENT_SHARDS}") - - is_wallet_generated = False - for _ in range(MAX_ITERATIONS_FOR_GENERATING_WALLET): - mnemonic = Mnemonic.generate() - pubkey = mnemonic.derive_key().generate_public_key() - generated_address_shard = get_shard_of_pubkey(pubkey.buffer, NUMBER_OF_SHARDS) - - if shard == generated_address_shard: - is_wallet_generated = True - break - - if not is_wallet_generated: - raise WalletGenerationError(f"Couldn't generate wallet in shard {shard}") - else: - mnemonic = Mnemonic.generate() - - # this is done to get rid of the Pylance error: possibly unbound - mnemonic = cast(Mnemonic, mnemonic) # type: ignore + mnemonic = _generate_mnemonic_with_shard_constraint(shard) print(f"Mnemonic: {mnemonic.get_text()}") print(f"Wallet address: {mnemonic.derive_key().generate_public_key().to_address(address_hrp).to_bech32()}") @@ -172,6 +152,24 @@ def wallet_new(args: Any): logger.info(f"Wallet ({format}) saved: {outfile}") +def _generate_mnemonic_with_shard_constraint(shard: Optional[int] = None) -> Mnemonic: + if shard is not None: + if shard not in CURRENT_SHARDS: + raise BadUserInput(f"Wrong shard provided. Choose between {CURRENT_SHARDS}") + + for _ in range(MAX_ITERATIONS_FOR_GENERATING_WALLET): + mnemonic = Mnemonic.generate() + pubkey = mnemonic.derive_key().generate_public_key() + generated_address_shard = get_shard_of_pubkey(pubkey.buffer, NUMBER_OF_SHARDS) + + if shard == generated_address_shard: + return mnemonic + + raise WalletGenerationError(f"Couldn't generate wallet in shard {shard}") + + return Mnemonic.generate() + + def convert_wallet(args: Any): infile = Path(args.infile).expanduser().resolve() if args.infile else None outfile = Path(args.outfile).expanduser().resolve() if args.outfile else None diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 8fa20a38..98885dc4 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, Sequence, Union +from typing import Any, List, Optional, Protocol, Union from multiversx_sdk import (Address, QueryRunnerAdapter, SmartContractQueriesController, @@ -8,13 +8,11 @@ TokenComputer, TokenTransfer, Transaction, TransactionPayload) from multiversx_sdk.abi import Abi -from multiversx_sdk.network_providers.interface import IContractQuery from multiversx_sdk_cli import errors from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.constants import DEFAULT_HRP from multiversx_sdk_cli.interfaces import IAddress -from multiversx_sdk_cli.utils import Object logger = logging.getLogger("contracts") @@ -29,37 +27,6 @@ def query_contract(self, query: Any) -> 'IContractQueryResponse': ... -class QueryResult(Object): - def __init__(self, as_base64: str, as_hex: str, as_number: Optional[int]): - self.base64 = as_base64 - self.hex = as_hex - self.number = as_number - - -class ContractQuery(IContractQuery): - def __init__(self, address: IAddress, function: str, value: int, arguments: List[bytes], caller: Optional[IAddress] = None): - self.contract = address - self.function = function - self.caller = caller - self.value = value - self.encoded_arguments = [item.hex() for item in arguments] - - def get_contract(self) -> IAddress: - return self.contract - - def get_function(self) -> str: - return self.function - - def get_encoded_arguments(self) -> Sequence[str]: - return self.encoded_arguments - - def get_caller(self) -> Optional[IAddress]: - return self.caller - - def get_value(self) -> int: - return self.value - - class IContractQueryResponse(Protocol): return_data: List[str] return_code: str @@ -201,7 +168,7 @@ def query_contract(self, contract_address: IAddress, proxy: INetworkProvider, function: str, - arguments: Union[List[Any], None], + arguments: Optional[List[Any]], should_prepare_args: bool) -> List[Any]: args = arguments if arguments else [] if should_prepare_args: diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 5859a4ef..746bf398 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -28,16 +28,11 @@ def resolve(name: str, proxy: INetworkProvider) -> Address: name_arg = "0x{}".format(str.encode(name).hex()) dns_address = dns_address_for_name(name) - chain_id = proxy.get_network_config().chain_id - config = TransactionsFactoryConfig(chain_id) - contract = SmartContract(config) - - response = contract.query_contract( + response = _query_contract( contract_address=dns_address, proxy=proxy, function="resolve", - arguments=[name_arg], - args_from_file=False + args=[name_arg] ) if len(response) == 0: @@ -51,17 +46,14 @@ 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) - chain_id = proxy.get_network_config().chain_id - config = TransactionsFactoryConfig(chain_id) - contract = SmartContract(config) - - response = contract.query_contract( + response = _query_contract( contract_address=dns_address, proxy=proxy, function="validateName", - arguments=[name_arg], - args_from_file=False - )[0] + args=[name_arg] + ) + + response = response[0] return_code = response["returnCode"] if return_code == "ok": @@ -105,17 +97,14 @@ def name_hash(name: str) -> bytes: def registration_cost(shard_id: int, proxy: INetworkProvider) -> int: dns_address = compute_dns_address_for_shard_id(shard_id) - chain_id = proxy.get_network_config().chain_id - config = TransactionsFactoryConfig(chain_id) - contract = SmartContract(config) - - response = contract.query_contract( + response = _query_contract( contract_address=dns_address, proxy=proxy, - function="getRegistrationCost", - arguments=[], - args_from_file=False - )[0] + function="versgetRegistrationCostion", + args=[] + ) + + response = response[0] data = response["returnDataParts"][0] if not data: @@ -127,17 +116,14 @@ 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) - chain_id = proxy.get_network_config().chain_id - config = TransactionsFactoryConfig(chain_id) - contract = SmartContract(config) - - response = contract.query_contract( + response = _query_contract( contract_address=dns_address, proxy=proxy, function="version", - arguments=[], - args_from_file=False - )[0] + args=[] + ) + + response = response[0] return bytearray.fromhex(response["returnDataParts"][0]).decode() @@ -161,3 +147,17 @@ 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()) + + +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 + ) diff --git a/multiversx_sdk_cli/utils.py b/multiversx_sdk_cli/utils.py index bc4d5b9c..a61e41fa 100644 --- a/multiversx_sdk_cli/utils.py +++ b/multiversx_sdk_cli/utils.py @@ -42,8 +42,6 @@ def default(self, o: Any) -> Any: return o.to_dictionary() if isinstance(o, bytes): return o.hex() - if isinstance(o, list): - return [self.default(item) for item in o] # type: ignore return super().default(o) From 650a8e6a57f69c26529728a2c905f579113617cd Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 29 Jul 2024 11:56:16 +0300 Subject: [PATCH 58/82] fixes after review --- multiversx_sdk_cli/cli_wallet.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 06205f68..eeae9ebb 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -115,7 +115,10 @@ def wallet_new(args: Any): address_hrp = args.address_hrp shard = args.shard - mnemonic = _generate_mnemonic_with_shard_constraint(shard) + if shard is not None: + mnemonic = _generate_mnemonic_with_shard_constraint(shard) + else: + mnemonic = Mnemonic.generate() print(f"Mnemonic: {mnemonic.get_text()}") print(f"Wallet address: {mnemonic.derive_key().generate_public_key().to_address(address_hrp).to_bech32()}") @@ -152,22 +155,20 @@ def wallet_new(args: Any): logger.info(f"Wallet ({format}) saved: {outfile}") -def _generate_mnemonic_with_shard_constraint(shard: Optional[int] = None) -> Mnemonic: - if shard is not None: - if shard not in CURRENT_SHARDS: - raise BadUserInput(f"Wrong shard provided. Choose between {CURRENT_SHARDS}") +def _generate_mnemonic_with_shard_constraint(shard: int) -> Mnemonic: - for _ in range(MAX_ITERATIONS_FOR_GENERATING_WALLET): - mnemonic = Mnemonic.generate() - pubkey = mnemonic.derive_key().generate_public_key() - generated_address_shard = get_shard_of_pubkey(pubkey.buffer, NUMBER_OF_SHARDS) + if shard not in CURRENT_SHARDS: + raise BadUserInput(f"Wrong shard provided. Choose between {CURRENT_SHARDS}") - if shard == generated_address_shard: - return mnemonic + for _ in range(MAX_ITERATIONS_FOR_GENERATING_WALLET): + mnemonic = Mnemonic.generate() + pubkey = mnemonic.derive_key().generate_public_key() + generated_address_shard = get_shard_of_pubkey(pubkey.buffer, NUMBER_OF_SHARDS) - raise WalletGenerationError(f"Couldn't generate wallet in shard {shard}") + if shard == generated_address_shard: + return mnemonic - return Mnemonic.generate() + raise WalletGenerationError(f"Couldn't generate wallet in shard {shard}") def convert_wallet(args: Any): From c86bfff8b4e07cef1c8c76d47d653402c94ebf28 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 29 Jul 2024 18:39:43 +0300 Subject: [PATCH 59/82] fix function name --- multiversx_sdk_cli/dns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 746bf398..114b1a11 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -100,7 +100,7 @@ def registration_cost(shard_id: int, proxy: INetworkProvider) -> int: response = _query_contract( contract_address=dns_address, proxy=proxy, - function="versgetRegistrationCostion", + function="getRegistrationCost", args=[] ) From 37151e7416cbb0323bb4756f713d9175fb9e617f Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 30 Jul 2024 10:51:14 +0300 Subject: [PATCH 60/82] use gateway for testing contract flow --- multiversx_sdk_cli/tests/test_cli_contracts.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index 12e4c523..f98a15f8 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -213,7 +213,7 @@ def test_contract_flow(capsys: Any): "--pem", alice, "--recall-nonce", "--gas-limit", "5000000", - "--proxy", "https://testnet-api.multiversx.com", + "--proxy", "https://testnet-gateway.multiversx.com", "--arguments", "0", "--send", "--wait-result" ]) @@ -225,7 +225,7 @@ def test_contract_flow(capsys: Any): main([ "contract", "query", contract, "--function", "getSum", - "--proxy", "https://testnet-api.multiversx.com" + "--proxy", "https://testnet-gateway.multiversx.com" ]) response = get_query_response(capsys) assert len(response) == 1 @@ -240,7 +240,7 @@ def test_contract_flow(capsys: Any): "--function", "add", "--recall-nonce", "--gas-limit", "5000000", - "--proxy", "https://testnet-api.multiversx.com", + "--proxy", "https://testnet-gateway.multiversx.com", "--arguments", "7", "--send", "--wait-result" ]) @@ -251,7 +251,7 @@ def test_contract_flow(capsys: Any): main([ "contract", "query", contract, "--function", "getSum", - "--proxy", "https://testnet-api.multiversx.com" + "--proxy", "https://testnet-gateway.multiversx.com" ]) response = get_query_response(capsys) assert len(response) == 1 @@ -266,7 +266,7 @@ def test_contract_flow(capsys: Any): "--pem", alice, "--recall-nonce", "--gas-limit", "5000000", - "--proxy", "https://testnet-api.multiversx.com", + "--proxy", "https://testnet-gateway.multiversx.com", "--arguments", "0", "--send", "--wait-result" ]) From f3168b8fe697a835def79da6ea1e313f74e7a2f6 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 30 Jul 2024 11:41:14 +0300 Subject: [PATCH 61/82] revert to using api --- multiversx_sdk_cli/tests/test_cli_contracts.py | 10 +++++----- multiversx_sdk_cli/tests/test_shared.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index f98a15f8..12e4c523 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -213,7 +213,7 @@ def test_contract_flow(capsys: Any): "--pem", alice, "--recall-nonce", "--gas-limit", "5000000", - "--proxy", "https://testnet-gateway.multiversx.com", + "--proxy", "https://testnet-api.multiversx.com", "--arguments", "0", "--send", "--wait-result" ]) @@ -225,7 +225,7 @@ def test_contract_flow(capsys: Any): main([ "contract", "query", contract, "--function", "getSum", - "--proxy", "https://testnet-gateway.multiversx.com" + "--proxy", "https://testnet-api.multiversx.com" ]) response = get_query_response(capsys) assert len(response) == 1 @@ -240,7 +240,7 @@ def test_contract_flow(capsys: Any): "--function", "add", "--recall-nonce", "--gas-limit", "5000000", - "--proxy", "https://testnet-gateway.multiversx.com", + "--proxy", "https://testnet-api.multiversx.com", "--arguments", "7", "--send", "--wait-result" ]) @@ -251,7 +251,7 @@ def test_contract_flow(capsys: Any): main([ "contract", "query", contract, "--function", "getSum", - "--proxy", "https://testnet-gateway.multiversx.com" + "--proxy", "https://testnet-api.multiversx.com" ]) response = get_query_response(capsys) assert len(response) == 1 @@ -266,7 +266,7 @@ def test_contract_flow(capsys: Any): "--pem", alice, "--recall-nonce", "--gas-limit", "5000000", - "--proxy", "https://testnet-gateway.multiversx.com", + "--proxy", "https://testnet-api.multiversx.com", "--arguments", "0", "--send", "--wait-result" ]) diff --git a/multiversx_sdk_cli/tests/test_shared.py b/multiversx_sdk_cli/tests/test_shared.py index 5df6c58f..caa43b3c 100644 --- a/multiversx_sdk_cli/tests/test_shared.py +++ b/multiversx_sdk_cli/tests/test_shared.py @@ -19,7 +19,7 @@ def test_prepare_chain_id_in_args(): prepare_chain_id_in_args(args) args.chain = "I" - args.proxy = "https://testnet-gateway.multiversx.com" + args.proxy = "https://testnet-api.multiversx.com" prepare_chain_id_in_args(args) assert args.chain == "T" From 09ab80a1830b5c53994f0f17f463a36cd715f07c Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 16 Sep 2024 11:30:18 +0300 Subject: [PATCH 62/82] remove macOS from build workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 012b4f9f..1707a6d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest] python-version: [3.11] steps: From 536e2b51b55efe09a9ff2c921170abb381629874 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 16 Sep 2024 11:56:13 +0300 Subject: [PATCH 63/82] re-added macOS for build workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1707a6d0..8360272f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-latest, macos-13, macos-13-xlarge] python-version: [3.11] steps: From 587bac11f6ddfc5063cf8fd0bfccb1088156d6df Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 16 Sep 2024 13:52:59 +0300 Subject: [PATCH 64/82] fixes after review --- multiversx_sdk_cli/config.py | 2 +- multiversx_sdk_cli/dependencies/modules.py | 7 ++++--- multiversx_sdk_cli/projects/core.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 1ed612fc..3b54049f 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -32,7 +32,7 @@ class MetaChainSystemSCsCost: def get_dependency_resolution(key: str) -> str: try: return get_value(f"dependencies.{key}.resolution") - except: + except Exception: return "" diff --git a/multiversx_sdk_cli/dependencies/modules.py b/multiversx_sdk_cli/dependencies/modules.py index 81a500b9..6e778f44 100644 --- a/multiversx_sdk_cli/dependencies/modules.py +++ b/multiversx_sdk_cli/dependencies/modules.py @@ -255,7 +255,7 @@ def _check_install_env(self, apply_correction: bool = True): This may cause problems with the installation.""") if apply_correction: - show_warning(f"CARGO_HOME will be temporarily unset.") + show_warning("CARGO_HOME will be temporarily unset.") os.environ["CARGO_HOME"] = "" if current_rustup_home: @@ -263,7 +263,7 @@ def _check_install_env(self, apply_correction: bool = True): This may cause problems with the installation of rust.""") if apply_correction: - show_warning(f"RUSTUP_HOME will be temporarily unset.") + show_warning("RUSTUP_HOME will be temporarily unset.") os.environ["RUSTUP_HOME"] = "" def _install_rust(self, tag: str) -> None: @@ -312,9 +312,10 @@ def _install_twiggy(self): def _install_sc_meta_deps(self): # this is needed for sc-meta to run the tests - # if os is Windows skip insallation + # 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.") diff --git a/multiversx_sdk_cli/projects/core.py b/multiversx_sdk_cli/projects/core.py index df61f980..1a663731 100644 --- a/multiversx_sdk_cli/projects/core.py +++ b/multiversx_sdk_cli/projects/core.py @@ -2,7 +2,7 @@ from pathlib import Path from typing import Any, List -from multiversx_sdk_cli import dependencies, errors, guards +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 952cd34e3e8058a6ad29b6ef3b4556b7ff8a18fe Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 16 Sep 2024 15:23:35 +0300 Subject: [PATCH 65/82] update requirements --- pyproject.toml | 4 ++-- requirements.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a87f87be..0bc30b98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,14 +21,14 @@ classifiers = [ dependencies = [ "toml>=0.10.2", - "requests", + "requests>=2.32.0,<3.0.0", "prettytable", "ledgercomm[hid]", "semver", "requests-cache", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk==0.11.0b0" + "multiversx-sdk==0.13.0" ] [project.scripts] diff --git a/requirements.txt b/requirements.txt index 987c82e3..2048553c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ toml>=0.10.2 types-toml -requests~=2.31.0 +requests>=2.32.0,<3.0.0 types-requests prettytable types-prettytable @@ -10,4 +10,4 @@ requests-cache rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk==0.11.0b0 +multiversx-sdk==0.13.0 From bede269edda646c6c81c6e914fc94ca1ca7fb00f Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 16 Sep 2024 16:47:34 +0300 Subject: [PATCH 66/82] mark mxpy-up.py as deprecated --- mxpy-up.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/mxpy-up.py b/mxpy-up.py index 8cfacc3e..8c9a5c7b 100644 --- a/mxpy-up.py +++ b/mxpy-up.py @@ -21,9 +21,15 @@ def main(): 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_false", default=True, 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() + if args.ignore_deprecation: + 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.") + logger.warning("To install using 'mxpy-up.py' set the `--ignore-deprecation` flag.") + return + exact_version = args.exact_version from_branch = args.from_branch interactive = not args.not_interactive @@ -56,6 +62,8 @@ def main(): 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).") @@ -260,7 +268,7 @@ def guide_system_path_integration(): return old_export_directive = f'export PATH="{Path("~/elrondsdk").expanduser()}:$PATH"\t# elrond-sdk' - new_export_directive = f'export PATH="${{HOME}}/multiversx-sdk:$PATH"\t# multiversx-sdk' + new_export_directive = 'export PATH="${{HOME}}/multiversx-sdk:$PATH"\t# multiversx-sdk' profile_files = get_profile_files() @@ -292,7 +300,7 @@ def guide_system_path_integration(): Your shell profile files: {profile_files_formatted} -The entry (entries) to remove: +The entry (entries) to remove: {old_export_directive} ############################################################################### Make sure you UNDERSTAND the above before proceeding further. @@ -306,12 +314,12 @@ def guide_system_path_integration(): ############################################################################### 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). +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): +The entry to check (it should be present): {new_export_directive}. ############################################################################### Make sure you UNDERSTAND the above before proceeding further. From acad3fc90a41c0d14e5c60cef916e18104be8ebc Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Sep 2024 10:46:37 +0300 Subject: [PATCH 67/82] fixes after review --- mxpy-up.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mxpy-up.py b/mxpy-up.py index 8c9a5c7b..20a3f33d 100644 --- a/mxpy-up.py +++ b/mxpy-up.py @@ -25,10 +25,10 @@ def main(): 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 args.ignore_deprecation: - 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.") - logger.warning("To install using 'mxpy-up.py' set the `--ignore-deprecation` flag.") - return + 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 mandatory, provide the `--ignore-deprecation` flag.") exact_version = args.exact_version from_branch = args.from_branch From e0aaf75565460e60300bec6de1e281743eda43bb Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Sep 2024 10:49:41 +0300 Subject: [PATCH 68/82] fixes after review --- mxpy-up.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mxpy-up.py b/mxpy-up.py index 20a3f33d..fd818b53 100644 --- a/mxpy-up.py +++ b/mxpy-up.py @@ -21,13 +21,13 @@ def main(): 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_false", default=True, help="'mxpy-up.py' is obsolete, install using 'pipx': https://docs.multiversx.com/sdk-and-tools/sdk-py/installing-mxpy/#install-using-pipx") + 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 args.ignore_deprecation: + 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 mandatory, provide the `--ignore-deprecation` flag.") exact_version = args.exact_version From 09801f71368e72703650578f207d4e74b7711962 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Sep 2024 12:50:32 +0300 Subject: [PATCH 69/82] add amount for unjailing transactions & ask user to install sc-meta deps --- multiversx_sdk_cli/cli_delegation.py | 2 +- multiversx_sdk_cli/delegation/staking_provider.py | 4 +++- multiversx_sdk_cli/dependencies/modules.py | 4 ++-- multiversx_sdk_cli/tests/test_cli_staking_provider.py | 2 ++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 9c49d158..eef8a357 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -171,7 +171,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: "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=f"service fee as hundredths of percents. (e.g. a service fee of 37.45 percent is expressed by the integer 3745)") + 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) diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation/staking_provider.py index 4d4834bb..418418dc 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -171,11 +171,13 @@ def prepare_transaction_for_unjailing_nodes(self, owner: IAccount, args: Any) -> delegation_contract = Address.new_from_bech32(args.delegation_contract) public_keys = self._load_validators_public_keys(args) + amount = int(args.value) tx = self._factory.create_transaction_for_unjailing_nodes( sender=owner.address, delegation_contract=delegation_contract, - public_keys=public_keys + public_keys=public_keys, + amount=amount ) tx.nonce = int(args.nonce) tx.version = int(args.version) diff --git a/multiversx_sdk_cli/dependencies/modules.py b/multiversx_sdk_cli/dependencies/modules.py index 6e778f44..7db8c1a5 100644 --- a/multiversx_sdk_cli/dependencies/modules.py +++ b/multiversx_sdk_cli/dependencies/modules.py @@ -10,7 +10,7 @@ myprocess, utils, workstation) from multiversx_sdk_cli.dependencies.resolution import ( DependencyResolution, get_dependency_resolution) -from multiversx_sdk_cli.ux import show_warning +from multiversx_sdk_cli.ux import show_message, show_warning logger = logging.getLogger("modules") @@ -241,7 +241,7 @@ def install(self, overwrite: bool) -> None: self._install_sc_meta() self._install_wasm_opt() self._install_twiggy() - self._install_sc_meta_deps() + show_message("Please also install all the necessary dependencies required for `sc-meta` to run properly bu running the following command: `sc-meta install all`.") def _check_install_env(self, apply_correction: bool = True): """ diff --git a/multiversx_sdk_cli/tests/test_cli_staking_provider.py b/multiversx_sdk_cli/tests/test_cli_staking_provider.py index 6921fe4b..a657c0d1 100644 --- a/multiversx_sdk_cli/tests/test_cli_staking_provider.py +++ b/multiversx_sdk_cli/tests/test_cli_staking_provider.py @@ -223,6 +223,7 @@ def test_unjail_nodes(capsys: Any): "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" @@ -235,6 +236,7 @@ def test_unjail_nodes(capsys: Any): assert transaction["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" assert transaction["receiver"] == "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqthllllsy5r6rh" assert transaction["gasLimit"] == 13645500 + assert transaction["value"] == "5000000000000000000" def test_change_service_fee(capsys: Any): From 47111f826c49e317640e8e496f54f474a67a61db Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Sep 2024 12:54:05 +0300 Subject: [PATCH 70/82] small fix --- mxpy-up.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mxpy-up.py b/mxpy-up.py index fd818b53..24177d3a 100644 --- a/mxpy-up.py +++ b/mxpy-up.py @@ -28,7 +28,7 @@ def main(): 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 mandatory, provide the `--ignore-deprecation` flag.") + 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 From f3daf91852abb59f99f83604246c70602d7bee79 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Sep 2024 14:49:20 +0300 Subject: [PATCH 71/82] fix flake errors --- multiversx_sdk_cli/cli_config.py | 2 +- multiversx_sdk_cli/docker.py | 2 +- .../ledger/ledger_app_handler.py | 2 +- multiversx_sdk_cli/localnet/config_part.py | 4 ++-- multiversx_sdk_cli/simulation.py | 2 ++ .../tests/local_verify_server.py | 4 ++-- multiversx_sdk_cli/validators/__init__.py | 21 +++++++------------ multiversx_sdk_cli/version.py | 2 +- 8 files changed, 18 insertions(+), 21 deletions(-) diff --git a/multiversx_sdk_cli/cli_config.py b/multiversx_sdk_cli/cli_config.py index d1439ac2..4e03879d 100644 --- a/multiversx_sdk_cli/cli_config.py +++ b/multiversx_sdk_cli/cli_config.py @@ -103,7 +103,7 @@ def list_configs(args: Any): def delete_config(args: Any): config_file = config.resolve_config_path() if not config_file.is_file(): - logger.info(f"Config file not found. Aborting...") + logger.info("Config file not found. Aborting...") return confirm_continuation(f"The file `{str(config_file)}` will be deleted. Do you want to continue? (y/n)") diff --git a/multiversx_sdk_cli/docker.py b/multiversx_sdk_cli/docker.py index b14e9ef2..02deb8bb 100644 --- a/multiversx_sdk_cli/docker.py +++ b/multiversx_sdk_cli/docker.py @@ -18,7 +18,7 @@ def is_docker_installed(): return True else: return False - except: + except Exception: logger.error("Something went wrong when checking if docker is installed!") diff --git a/multiversx_sdk_cli/ledger/ledger_app_handler.py b/multiversx_sdk_cli/ledger/ledger_app_handler.py index dde814ec..2ec9e955 100644 --- a/multiversx_sdk_cli/ledger/ledger_app_handler.py +++ b/multiversx_sdk_cli/ledger/ledger_app_handler.py @@ -30,7 +30,7 @@ class LedgerApp: def __init__(self): try: self.transport = Transport(interface="hid", debug=False) # Nano S/X using HID interface - except: + except Exception: raise LedgerError(CONNECTION_ERROR_MSG) def close(self): diff --git a/multiversx_sdk_cli/localnet/config_part.py b/multiversx_sdk_cli/localnet/config_part.py index e2df004f..cccdd17f 100644 --- a/multiversx_sdk_cli/localnet/config_part.py +++ b/multiversx_sdk_cli/localnet/config_part.py @@ -24,8 +24,8 @@ def _validate_overriding_entries(self, overriding: Dict[str, Any]) -> None: if unknown_entries: logger.error(f"""\ -Unknown localnet configuration entries: {unknown_entries}. -Please check the configuration of the localnet. +Unknown localnet configuration entries: {unknown_entries}. +Please check the configuration of the localnet. For "{self.get_name()}", the allowed entries are: {allowed_entries}.""") raise UnknownConfigurationError(f"Unknown localnet configuration entries: {unknown_entries}") diff --git a/multiversx_sdk_cli/simulation.py b/multiversx_sdk_cli/simulation.py index a6b1bbd4..bd5c41c6 100644 --- a/multiversx_sdk_cli/simulation.py +++ b/multiversx_sdk_cli/simulation.py @@ -1,5 +1,6 @@ from collections import OrderedDict from typing import Any, Dict, Protocol + from multiversx_sdk_cli.interfaces import ISimulateResponse, ITransaction from multiversx_sdk_cli.utils import ISerializable @@ -19,6 +20,7 @@ def to_dictionary(self) -> Dict[str, Any]: return dictionary + class Simulator(): def __init__(self, proxy: INetworkProvider) -> None: self.proxy = proxy diff --git a/multiversx_sdk_cli/tests/local_verify_server.py b/multiversx_sdk_cli/tests/local_verify_server.py index 010177c3..dbc989ab 100644 --- a/multiversx_sdk_cli/tests/local_verify_server.py +++ b/multiversx_sdk_cli/tests/local_verify_server.py @@ -1,5 +1,5 @@ -from http.server import HTTPServer, BaseHTTPRequestHandler import json +from http.server import BaseHTTPRequestHandler, HTTPServer HOST = 'localhost' PORT = 7777 @@ -14,7 +14,7 @@ def do_POST(self): if self.path == "/initialise": 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')) diff --git a/multiversx_sdk_cli/validators/__init__.py b/multiversx_sdk_cli/validators/__init__.py index c66ffcba..74a11e54 100644 --- a/multiversx_sdk_cli/validators/__init__.py +++ b/multiversx_sdk_cli/validators/__init__.py @@ -1,16 +1,11 @@ -from multiversx_sdk_cli.validators.core import (prepare_args_for_change_reward_address, - prepare_args_for_claim, - prepare_args_for_stake, - prepare_args_for_unbond, - prepare_args_for_unjail, - prepare_args_for_unstake, - prepare_args_for_unstake_nodes, - prepare_args_for_unstake_tokens, - prepare_args_for_unbond_tokens, - prepare_args_for_unbond_nodes, - prepare_args_for_clean_registered_data, - prepare_args_for_restake_unstaked_nodes) - +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", diff --git a/multiversx_sdk_cli/version.py b/multiversx_sdk_cli/version.py index fa7c33f3..df09e4ce 100644 --- a/multiversx_sdk_cli/version.py +++ b/multiversx_sdk_cli/version.py @@ -9,7 +9,7 @@ def get_version() -> str: try: # Works for local development return _get_version_from_pyproject() - except Exception as error: + except Exception: try: # Works for the installed package return _get_version_from_metadata() From 8d4e014e2f919b8428c539e8cb6dbabaebdf7819 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Sep 2024 15:10:32 +0300 Subject: [PATCH 72/82] update requirements --- pyproject.toml | 4 ++-- requirements.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ac423476..33a016c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,13 +21,13 @@ classifiers = [ dependencies = [ "toml>=0.10.2", - "requests", + "requests>=2.32.0,<3.0.0", "prettytable", "ledgercomm[hid]", "semver", "requests-cache", "rich==13.3.4", - "multiversx-sdk>=0.9.2,<1.0.0", + "multiversx-sdk==0.13.0", "argcomplete==3.2.2" ] diff --git a/requirements.txt b/requirements.txt index c6f46432..2048553c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ toml>=0.10.2 types-toml -requests~=2.31.0 +requests>=2.32.0,<3.0.0 types-requests prettytable types-prettytable @@ -10,4 +10,4 @@ requests-cache rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk>=0.9.2,<1.0.0 +multiversx-sdk==0.13.0 From eea5966076a030a3f925c4e9f45884605bc454f7 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Sep 2024 16:00:45 +0300 Subject: [PATCH 73/82] revert alteration of installing sc-meta dependencies --- multiversx_sdk_cli/dependencies/modules.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk_cli/dependencies/modules.py b/multiversx_sdk_cli/dependencies/modules.py index 7db8c1a5..6e778f44 100644 --- a/multiversx_sdk_cli/dependencies/modules.py +++ b/multiversx_sdk_cli/dependencies/modules.py @@ -10,7 +10,7 @@ myprocess, utils, workstation) from multiversx_sdk_cli.dependencies.resolution import ( DependencyResolution, get_dependency_resolution) -from multiversx_sdk_cli.ux import show_message, show_warning +from multiversx_sdk_cli.ux import show_warning logger = logging.getLogger("modules") @@ -241,7 +241,7 @@ def install(self, overwrite: bool) -> None: self._install_sc_meta() self._install_wasm_opt() self._install_twiggy() - show_message("Please also install all the necessary dependencies required for `sc-meta` to run properly bu running the following command: `sc-meta install all`.") + self._install_sc_meta_deps() def _check_install_env(self, apply_correction: bool = True): """ From 2a4fbbe777215d62ae2b6e69a372a0c4f072fe49 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Sep 2024 16:04:06 +0300 Subject: [PATCH 74/82] show message to install sc-meta deps --- multiversx_sdk_cli/dependencies/modules.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk_cli/dependencies/modules.py b/multiversx_sdk_cli/dependencies/modules.py index 6e778f44..10bd5c05 100644 --- a/multiversx_sdk_cli/dependencies/modules.py +++ b/multiversx_sdk_cli/dependencies/modules.py @@ -10,7 +10,7 @@ myprocess, utils, workstation) from multiversx_sdk_cli.dependencies.resolution import ( DependencyResolution, get_dependency_resolution) -from multiversx_sdk_cli.ux import show_warning +from multiversx_sdk_cli.ux import show_message, show_warning logger = logging.getLogger("modules") @@ -241,7 +241,7 @@ def install(self, overwrite: bool) -> None: self._install_sc_meta() self._install_wasm_opt() self._install_twiggy() - self._install_sc_meta_deps() + show_message("Please also install all the necessary dependencies required for `sc-meta` to run properly by running the following command: `sc-meta install all`.") def _check_install_env(self, apply_correction: bool = True): """ From ce3892db37f68e27705dbf70b0914a0f452e4d21 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Sep 2024 17:01:39 +0300 Subject: [PATCH 75/82] deprecate report command --- multiversx_sdk_cli/cli_contracts.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index ab711caf..b9a76122 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -21,7 +21,7 @@ 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 +from multiversx_sdk_cli.ux import show_message, show_warning logger = logging.getLogger("cli.contracts") @@ -302,10 +302,15 @@ 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) + check_if_rust_is_installed() args_dict = args.__dict__ projects.do_report(args, args_dict) + show_warning(deprecation_message) + def run_tests(args: Any): check_if_rust_is_installed() From 0820759a8e8588024bc7a5ed67d0f0f90d28fec5 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Sep 2024 17:29:53 +0300 Subject: [PATCH 76/82] advise users to use sc-meta all build --- multiversx_sdk_cli/cli_contracts.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index ab711caf..e13a007c 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -21,7 +21,7 @@ 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 +from multiversx_sdk_cli.ux import show_message, show_warning logger = logging.getLogger("cli.contracts") @@ -293,16 +293,16 @@ def clean(args: Any): def build(args: Any): - check_if_rust_is_installed() 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.") + def do_report(args: Any): - check_if_rust_is_installed() args_dict = args.__dict__ projects.do_report(args, args_dict) From 4ceac173da797b2af9a977e93d25b18c0c4b0a5d Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 18 Sep 2024 09:43:54 +0300 Subject: [PATCH 77/82] change message --- multiversx_sdk_cli/dependencies/modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/dependencies/modules.py b/multiversx_sdk_cli/dependencies/modules.py index 10bd5c05..3455b24d 100644 --- a/multiversx_sdk_cli/dependencies/modules.py +++ b/multiversx_sdk_cli/dependencies/modules.py @@ -241,7 +241,7 @@ def install(self, overwrite: bool) -> None: self._install_sc_meta() self._install_wasm_opt() self._install_twiggy() - show_message("Please also install all the necessary dependencies required for `sc-meta` to run properly by running 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): """ From ed6d7a668b0e25cdb0b90c8079b0c975e6ad12f9 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 19 Sep 2024 09:24:04 +0300 Subject: [PATCH 78/82] bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c3e5815f..c90a4f59 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "multiversx-sdk-cli" -version = "9.6.2" +version = "9.7.0" authors = [ { name="MultiversX" }, ] From cb32a683199d08b0d6aa6ff40b6f45ca163eecdd Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 19 Sep 2024 15:53:54 +0300 Subject: [PATCH 79/82] access token fixes --- multiversx_sdk_cli/cli_faucet.py | 12 ++++-------- multiversx_sdk_cli/native_auth_client.py | 6 +++--- multiversx_sdk_cli/tests/test_native_auth_client.py | 6 +++--- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/multiversx_sdk_cli/cli_faucet.py b/multiversx_sdk_cli/cli_faucet.py index f5d40a63..878dc7ad 100644 --- a/multiversx_sdk_cli/cli_faucet.py +++ b/multiversx_sdk_cli/cli_faucet.py @@ -3,8 +3,6 @@ from enum import Enum from typing import Any, List, Tuple -from multiversx_sdk import Message, MessageComputer - from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.errors import BadUserInput from multiversx_sdk_cli.native_auth_client import (NativeAuthClient, @@ -44,10 +42,8 @@ def faucet(args: Any): client = NativeAuthClient(config) init_token = client.initialize() - message = Message(f"{account.address.to_bech32()}{init_token}".encode()) - - message_computer = MessageComputer() - signature = account.sign_message(message_computer.compute_bytes_for_signing(message)) + 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(), @@ -56,10 +52,10 @@ def faucet(args: Any): ) logger.info(f"Requesting funds for address: {account.address.to_bech32()}") - call_web_Wallet_faucet(wallet_url=wallet, access_token=access_token) + call_web_wallet_faucet(wallet_url=wallet, access_token=access_token) -def call_web_Wallet_faucet(wallet_url: str, access_token: str): +def call_web_wallet_faucet(wallet_url: str, access_token: str): faucet_url = f"{wallet_url}/faucet?accessToken={access_token}" webbrowser.open_new_tab(faucet_url) diff --git a/multiversx_sdk_cli/native_auth_client.py b/multiversx_sdk_cli/native_auth_client.py index f5c48372..60bcb55c 100644 --- a/multiversx_sdk_cli/native_auth_client.py +++ b/multiversx_sdk_cli/native_auth_client.py @@ -6,7 +6,7 @@ from multiversx_sdk_cli.errors import NativeAuthClientError -EXPIRY_TIME_IN_SECONDS = 60 * 60 * 24 +EXPIRY_TIME_IN_SECONDS = 60 * 60 * 2 DEFAULT_API_URL = "https://api.multiversx.com" @@ -29,8 +29,8 @@ def __init__( class NativeAuthClient: - def __init__(self, config: NativeAuthClientConfig = NativeAuthClientConfig()) -> None: - self.config = config + def __init__(self, config: Optional[NativeAuthClientConfig] = None) -> None: + self.config = config or NativeAuthClientConfig() def get_token(self, address: str, token: str, signature: str) -> str: encoded_address = self.encode_value(address) diff --git a/multiversx_sdk_cli/tests/test_native_auth_client.py b/multiversx_sdk_cli/tests/test_native_auth_client.py index 3eaadd16..92678941 100644 --- a/multiversx_sdk_cli/tests/test_native_auth_client.py +++ b/multiversx_sdk_cli/tests/test_native_auth_client.py @@ -36,7 +36,7 @@ class TestNativeAuth: def test_latest_block_should_return_signable_token(self, mocker: Any): mock(mocker, 200, [{"hash": self.BLOCK_HASH}]) - config = NativeAuthClientConfig(origin=self.ORIGIN) + config = NativeAuthClientConfig(origin=self.ORIGIN, expiry_seconds=self.TTL) client = NativeAuthClient(config) token = client.initialize() assert token == self.TOKEN @@ -54,7 +54,7 @@ def test_fallback_mechanism(self, mocker: Any): "error": "Bad request"}]) mock(mocker, 200, {"hash": self.BLOCK_HASH}) - config = NativeAuthClientConfig(origin=self.ORIGIN) + config = NativeAuthClientConfig(origin=self.ORIGIN, expiry_seconds=self.TTL) client = NativeAuthClient(config) token = client.initialize() @@ -85,7 +85,7 @@ def test_latest_block_should_return_signable_token(self, mocker: Any): ] mock_side_effect(mocker, responses) - config = NativeAuthClientConfig(origin=self.ORIGIN, gateway_url=self.GATEWAY, block_hash_shard=self.METASHARD) + 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 From 990fa9765a77a3d4ea033d6ae2362e103b1d45dc Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 19 Sep 2024 16:03:26 +0300 Subject: [PATCH 80/82] re-generate CLI.md file --- .github/workflows/build-windows.yml | 3 +- CLI.md | 188 +++++++++++++++++++++++++-- multiversx_sdk_cli/cli_delegation.py | 2 +- 3 files changed, 181 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 9a84e53b..e7010de2 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -30,8 +30,7 @@ jobs: run: | python3 -m pip install --upgrade pip pip3 install -r requirements.txt - pip3 install pytest - pip3 install pytest-mock + pip3 install -r requirements-dev.txt - name: Set github_api_token shell: bash run: | diff --git a/CLI.md b/CLI.md index 257acf69..3cf655de 100644 --- a/CLI.md +++ b/CLI.md @@ -70,7 +70,7 @@ new Create a new Smart Contract project based on a te templates List the available Smart Contract templates. build Build a Smart Contract project. clean Clean a Smart Contract project. -test Run scenarios (tests). +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). @@ -195,6 +195,7 @@ 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) @@ -220,10 +221,18 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --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 @@ -283,6 +292,7 @@ positional arguments: 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) --pem PEM 🔑 the PEM file, if keyfile not provided --pem-index PEM_INDEX 🔑 the index in the PEM file (default: 0) @@ -304,11 +314,19 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --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[..] + --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 ...] token transfers for transfer & execute, as [token, amount] E.g. --token-transfers NFT-123456-0a 1 ESDT-987654 100000000 @@ -372,6 +390,7 @@ positional arguments: 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) @@ -398,10 +417,18 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --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 @@ -433,11 +460,14 @@ positional arguments: options: -h, --help show this help message and exit + --abi ABI the ABI 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[..] + --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. E.g. [{ + 'to': 'erd1...', 'amount': 10000000000 }] ``` ### Contract.Report @@ -550,8 +580,17 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --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. + --token-transfers NFT-123456-0a 1 ESDT-987654 100000000 --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) @@ -694,6 +733,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -744,6 +789,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -792,6 +843,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -840,6 +897,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -888,6 +951,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -936,6 +1005,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -961,11 +1036,34 @@ usage: mxpy staking-provider COMMAND [-h] ... Staking provider omnitool COMMANDS: - {create-new-delegation-contract,get-contract-address,add-nodes,remove-nodes,stake-nodes,unbond-nodes,unstake-nodes,unjail-nodes,change-service-fee,modify-delegation-cap,automatic-activation,redelegate-cap,set-metadata} + {create-new-delegation-contract,get-contract-address,add-nodes,remove-nodes,stake-nodes,unbond-nodes,unstake-nodes,unjail-nodes,delegate,claim-rewards,redelegate-rewards,undelegate,withdraw,change-service-fee,modify-delegation-cap,automatic-activation,redelegate-cap,set-metadata,make-delegation-contract-from-validator} OPTIONS: -h, --help show this help message and exit +---------------- +COMMANDS summary +---------------- +create-new-delegation-contract Create a new delegation system smart contract, transferred value must be greater than baseIssuingCost + min deposit value +get-contract-address Get create contract address by transaction hash +add-nodes Add new nodes must be called by the contract owner +remove-nodes Remove nodes must be called by the contract owner +stake-nodes Stake nodes must be called by the contract owner +unbond-nodes Unbond nodes must be called by the contract owner +unstake-nodes Unstake nodes must be called by the contract owner +unjail-nodes Unjail nodes must be called by the contract owner +delegate Delegate funds to a delegation contract +claim-rewards Claim the rewards earned for delegating +redelegate-rewards Redelegate the rewards earned for delegating +undelegate Undelegate funds from a delegation contract +withdraw Withdraw funds from a delegation contract +change-service-fee Change service fee must be called by the contract owner +modify-delegation-cap Modify delegation cap must be called by the contract owner +automatic-activation Automatic activation must be called by the contract owner +redelegate-cap Redelegate cap must be called by the contract owner +set-metadata Set metadata must be called by the contract owner +make-delegation-contract-from-validator Create a delegation contract from validator data. Must be called by the node operator + ``` ### StakingProvider.CreateNewDelegationContract @@ -974,7 +1072,7 @@ OPTIONS: $ mxpy staking-provider create-new-delegation-contract --help usage: mxpy staking-provider create-new-delegation-contract [-h] ... -Create a new delegation system smart contract, transferred value must begreater than baseIssuingCost + min deposit value +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 @@ -999,6 +1097,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1045,7 +1149,6 @@ 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 - --using-delegation-manager whether delegation contract was created using the Delegation Manager --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) @@ -1067,6 +1170,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1117,6 +1226,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1167,6 +1282,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1217,6 +1338,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1267,6 +1394,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1317,6 +1450,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1366,6 +1505,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1415,6 +1560,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1465,6 +1616,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1515,6 +1672,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1566,6 +1729,12 @@ options: --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 + --relayer RELAYER the address of the relayer + --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided + when creating the relayer's transaction + --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE + where to save the transaction as an inner transaction (default: + stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1663,6 +1832,7 @@ options: (default: None) --address-hrp ADDRESS_HRP the human-readable part of the address, when format is keystore- secret-key or pem (default: erd) + --shard SHARD the shard in which the address will be generated; (default: random) ``` ### Wallet.Convert @@ -1894,11 +2064,11 @@ usage: mxpy deps install [-h] ... Install dependencies or multiversx-sdk modules. positional arguments: - {all,rust,golang,vmtools,testwallets} the dependency to install + {all,rust,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 @@ -1911,10 +2081,10 @@ usage: mxpy deps check [-h] ... Check whether a dependency is installed. positional arguments: - {all,rust,golang,vmtools,testwallets} the dependency to check + {all,rust,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** diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index c341a850..aafbaa4b 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -12,7 +12,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: # 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" + "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") From 01c19a33a660fb78419ffc50cd712e06ee985ff2 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 23 Sep 2024 14:42:10 +0300 Subject: [PATCH 81/82] fixes after review --- multiversx_sdk_cli/native_auth_client.py | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/multiversx_sdk_cli/native_auth_client.py b/multiversx_sdk_cli/native_auth_client.py index 60bcb55c..f7174b8e 100644 --- a/multiversx_sdk_cli/native_auth_client.py +++ b/multiversx_sdk_cli/native_auth_client.py @@ -6,7 +6,7 @@ from multiversx_sdk_cli.errors import NativeAuthClientError -EXPIRY_TIME_IN_SECONDS = 60 * 60 * 2 +DEFAULT_EXPIRY_TIME_IN_SECONDS = 60 * 60 * 2 DEFAULT_API_URL = "https://api.multiversx.com" @@ -15,7 +15,7 @@ def __init__( self, origin: str = '', api_url: str = DEFAULT_API_URL, - expiry_seconds: int = EXPIRY_TIME_IN_SECONDS, + 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 @@ -32,19 +32,19 @@ class NativeAuthClient: def __init__(self, config: Optional[NativeAuthClientConfig] = None) -> None: self.config = config or NativeAuthClientConfig() - 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 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) + 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() @@ -91,11 +91,11 @@ def _get_current_block_hash_using_api_fallback(self) -> str: response = self._execute_request(url) return response[0]["hash"] - def encode_value(self, string: str) -> str: + def _encode_value(self, string: str) -> str: encoded = base64.b64encode(string.encode('utf-8')).decode('utf-8') - return self.escape(encoded) + return self._escape(encoded) - def escape(self, string: str) -> str: + def _escape(self, string: str) -> str: return string.replace("+", "-").replace("/", "_").replace("=", "") def _execute_request(self, url: str) -> Any: From 19c7d98352af24298b8e5177f406d692dc04b1e1 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 23 Sep 2024 15:04:49 +0300 Subject: [PATCH 82/82] change OS to macos-latest --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8360272f..012b4f9f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest, macos-13, macos-13-xlarge] + os: [ubuntu-latest, macos-latest] python-version: [3.11] steps: