From 0fbb8724ce8c412b76eb591390a875ff8fb0fb44 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 12 Mar 2024 13:10:03 +0200 Subject: [PATCH 01/13] 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 02/13] 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 03/13] 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 04/13] 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 05/13] 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 06/13] 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 07/13] 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 030cf554861b888992b1a9bc56087751ada04804 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 1 Jul 2024 12:59:50 +0300 Subject: [PATCH 08/13] 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 25c1f32e553c19960ad1746a8568a34e7458a35e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 2 Jul 2024 12:11:04 +0300 Subject: [PATCH 09/13] 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 10/13] 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 cb32a683199d08b0d6aa6ff40b6f45ca163eecdd Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 19 Sep 2024 15:53:54 +0300 Subject: [PATCH 11/13] 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 12/13] 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 13/13] 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: