From 856641322689e42001cdd2fabe9c9dc0369d0aad Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 13 May 2025 13:33:36 +0300 Subject: [PATCH 01/64] wip: multisig integration --- multiversx_sdk_cli/cli.py | 2 + multiversx_sdk_cli/cli_multisig.py | 1057 ++++++++++++++++++++++++++++ multiversx_sdk_cli/multisig.py | 592 ++++++++++++++++ requirements.txt | 3 +- 4 files changed, 1653 insertions(+), 1 deletion(-) create mode 100644 multiversx_sdk_cli/cli_multisig.py create mode 100644 multiversx_sdk_cli/multisig.py diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 00519941..41d3641f 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -18,6 +18,7 @@ import multiversx_sdk_cli.cli_faucet import multiversx_sdk_cli.cli_ledger import multiversx_sdk_cli.cli_localnet +import multiversx_sdk_cli.cli_multisig import multiversx_sdk_cli.cli_transactions import multiversx_sdk_cli.cli_validator_wallet import multiversx_sdk_cli.cli_validators @@ -122,6 +123,7 @@ def setup_parser(args: list[str]): 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)) + commands.append(multiversx_sdk_cli.cli_multisig.setup_parser(args, subparsers)) parser.epilog = """ ---------------------- diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py new file mode 100644 index 00000000..868f85a2 --- /dev/null +++ b/multiversx_sdk_cli/cli_multisig.py @@ -0,0 +1,1057 @@ +import json +import logging +from pathlib import Path +from typing import Any + +from multiversx_sdk import ( + Address, + AddressComputer, + Token, + TokenComputer, + TokenTransfer, + Transaction, + TransactionsFactoryConfig, +) +from multiversx_sdk.abi import Abi + +from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.args_validation import ( + ensure_wallet_args_are_provided, + validate_broadcast_args, + validate_chain_id_args, + validate_transaction_args, +) +from multiversx_sdk_cli.cli_output import CLIOutputBuilder +from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS +from multiversx_sdk_cli.multisig import MultisigWrapper + +logger = logging.getLogger("cli.multisig") + + +def setup_parser(args: list[str], subparsers: Any) -> Any: + parser = cli_shared.add_group_subparser( + subparsers, + "multisig", + "Deploy and interact with the Multisig Smart Contract", + ) + subparsers = parser.add_subparsers() + + output_description = CLIOutputBuilder.describe( + with_contract=True, with_transaction_on_network=True, with_simulation=True + ) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "deploy", + f"Deploy a Multisig Smart Contract.{output_description}", + ) + _add_bytecode_arg(sub) + _add_abi_arg(sub) + sub.add_argument( + "--quorum", + type=int, + required=True, + help="the number of signatures required to approve a proposal", + ) + sub.add_argument( + "--board-members", + required=True, + nargs="+", + type=str, + help="the bech32 addresses of the board members", + ) + _add_metadata_arg(sub) + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=deploy) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "deposit", + f"Deposit native tokens (EGLD) or ESDT tokens into a Multisig Smart Contract.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + 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) + cli_shared.add_token_transfers_args(sub) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=deposit) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "discard-action", + f"Discard a proposed action. Signatures must be removed first via `unsign`.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + _add_action_id_arg(sub) + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=discard_action) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "discard-batch", + f"Discard all the actions for the specified IDs.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + sub.add_argument( + "--action-ids", + required=True, + nargs="+", + type=int, + help="the IDs of the actions to discard", + ) + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=discard_batch) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "add-board-member", + f"Propose adding a new board member.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + sub.add_argument( + "--board-member", + required=True, + type=str, + help="the bech32 address of the proposed board member", + ) + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=add_board_member) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "add-proposer", + f"Propose adding a new proposer.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + sub.add_argument( + "--proposer", + required=True, + type=str, + help="the bech32 address of the proposed proposer", + ) + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=add_proposer) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "remove-user", + f"Propose removing a user from the Multisig Smart Contract.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + sub.add_argument( + "--user", + required=True, + type=str, + help="the bech32 address of the proposed user to be removed", + ) + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=remove_user) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "change-quorum", + f"Propose changing the quorum of the Multisig Smart Contract.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + sub.add_argument( + "--quorum", + required=True, + type=int, + help="the size of the new quorum (number of signatures required to approve a proposal)", + ) + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=change_quorum) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "transfer-and-execute", + f"Propose transferring EGLD and optionally calling a smart contract.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + sub.add_argument( + "--opt-gas-limit", + type=int, + help="the size of the new quorum (number of signatures required to approve a proposal)", + ) + sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") + sub.add_argument("--function", type=str, help="the function to call") + _add_arguments_arg(sub) + 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=True, with_data=False) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=transfer_and_execute) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "transfer-and-execute-esdt", + f"Propose transferring ESDTs and optionally calling a smart contract.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_token_transfers_args(sub) + sub.add_argument( + "--opt-gas-limit", + type=int, + help="the size of the new quorum (number of signatures required to approve a proposal)", + ) + sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") + sub.add_argument("--function", type=str, help="the function to call") + _add_arguments_arg(sub) + 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=True, with_data=False) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=transfer_and_execute_esdt) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "async-call", + f"Propose a transaction in which the contract will perform an async call.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_token_transfers_args(sub) + sub.add_argument( + "--opt-gas-limit", + type=int, + help="the size of the new quorum (number of signatures required to approve a proposal)", + ) + sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") + sub.add_argument("--function", type=str, help="the function to call") + _add_arguments_arg(sub) + 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=True, with_data=False) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=async_call) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "deploy-from-source", + f"Propose a smart contract deploy from a previously deployed smart contract.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + + sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") + _add_arguments_arg(sub) + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=deploy_from_source) + + parser.epilog = cli_shared.build_group_epilog(subparsers) + return subparsers + + +def _add_bytecode_arg(sub: Any): + sub.add_argument( + "--bytecode", + type=str, + required=True, + help="the file containing the WASM bytecode", + ) + + +def _add_contract_arg(sub: Any): + sub.add_argument("--contract", required=True, type=str, help="🖄 the bech32 address of the Multisig Smart Contract") + + +def _add_abi_arg(sub: Any): + sub.add_argument("--abi", required=True, type=str, help="the ABI file of the Multisig Smart Contract") + + +def _add_action_id_arg(sub: Any): + sub.add_argument("--action", required=True, type=int, help="the id of the action") + + +def _add_metadata_arg(sub: Any): + sub.add_argument( + "--metadata-not-upgradeable", + dest="metadata_upgradeable", + action="store_false", + help="‼ mark the contract as NOT upgradeable (default: upgradeable)", + ) + sub.add_argument( + "--metadata-not-readable", + dest="metadata_readable", + action="store_false", + help="‼ mark the contract as NOT readable (default: readable)", + ) + sub.add_argument( + "--metadata-payable", + dest="metadata_payable", + action="store_true", + help="‼ mark the contract as payable (default: not payable)", + ) + sub.add_argument( + "--metadata-payable-by-sc", + dest="metadata_payable_by_sc", + action="store_true", + help="‼ mark the contract as payable by SC (default: not payable by SC)", + ) + sub.set_defaults(metadata_upgradeable=True, metadata_payable=False) + + +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 addr:erd1[..]", + ) + sub.add_argument( + "--arguments-file", + type=str, + help="a json file containing the arguments. ONLY if abi file is provided. " + "E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }]", + ) + + +def deploy(args: Any): + logger.debug("multisig.deploy") + + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + quorum = args.quorum + board_members = [Address.new_from_bech32(addr) for addr in args.board_members] + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + tx = multisig.prepare_deploy_transaction( + owner=sender, + nonce=sender.nonce, + bytecode=Path(args.bytecode), + quorum=quorum, + board_members=board_members, + upgradeable=args.metadata_upgradeable, + readable=args.metadata_readable, + payable=args.metadata_payable, + payable_by_sc=args.metadata_payable_by_sc, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + address_computer = AddressComputer(NUMBER_OF_SHARDS) + contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=tx.nonce) + + logger.info("Contract address: %s", contract_address.to_bech32()) + utils.log_explorer_contract_address(args.chain, contract_address.to_bech32()) + + _send_or_simulate(tx, contract_address, args) + + +def deposit(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + native_amount = int(args.value) + token_transfers = _prepare_token_transfers(args.token_transfers) + + tx = multisig.prepare_deposit_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + native_amount=native_amount, + token_transfers=token_transfers, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def discard_action(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) + + tx = multisig.prepare_discard_action_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + action_id=action_id, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def discard_batch(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + actions = args.action_ids + + tx = multisig.prepare_discard_batch_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + action_ids=actions, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def add_board_member(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + board_member = Address.new_from_bech32(args.board_member) + + tx = multisig.prepare_add_board_member_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + board_member=board_member, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def add_proposer(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + proposer = Address.new_from_bech32(args.proposer) + + tx = multisig.prepare_add_proposer_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + proposer=proposer, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def remove_user(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + user = Address.new_from_bech32(args.user) + + tx = multisig.prepare_remove_user_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + user=user, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def change_quorum(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + quorum = int(args.quorum) + + tx = multisig.prepare_change_quorum_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + quorum=quorum, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def transfer_and_execute(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + + receiver = Address.new_from_bech32(args.receiver) + opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None + function = args.function if args.function else None + contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None + arguments, should_prepare_args = _get_contract_arguments(args) + + tx = multisig.prepare_transfer_execute_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + receiver=receiver, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + should_prepare_args=should_prepare_args, + guardian_and_relayer_data=guardian_and_relayer_data, + opt_gas_limit=opt_gas_limit, + function=function, + abi=contract_abi, + arguments=arguments, + native_token_amount=int(args.value), + ) + + _send_or_simulate(tx, contract, args) + + +def transfer_and_execute_esdt(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + if int(args.value) != 0: + raise Exception("Native token transfer is not allowed for this command.") + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + + receiver = Address.new_from_bech32(args.receiver) + opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None + function = args.function if args.function else None + contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None + arguments, should_prepare_args = _get_contract_arguments(args) + token_transfers = _prepare_token_transfers(args.token_transfers) + + tx = multisig.prepare_transfer_execute_esdt_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + receiver=receiver, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + should_prepare_args=should_prepare_args, + guardian_and_relayer_data=guardian_and_relayer_data, + token_transfers=token_transfers, + opt_gas_limit=opt_gas_limit, + function=function, + abi=contract_abi, + arguments=arguments, + ) + + _send_or_simulate(tx, contract, args) + + +def async_call(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + + receiver = Address.new_from_bech32(args.receiver) + opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None + function = args.function if args.function else None + contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None + arguments, should_prepare_args = _get_contract_arguments(args) + token_transfers = _prepare_token_transfers(args.token_transfers) + + tx = multisig.prepare_async_call_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + receiver=receiver, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + should_prepare_args=should_prepare_args, + guardian_and_relayer_data=guardian_and_relayer_data, + native_token_amount=int(args.value), + token_transfers=token_transfers, + opt_gas_limit=opt_gas_limit, + function=function, + abi=contract_abi, + arguments=arguments, + ) + + _send_or_simulate(tx, contract, args) + + +def deploy_from_source(args: Any): + pass + # validate_transaction_args(args) + # ensure_wallet_args_are_provided(args) + # validate_broadcast_args(args) + # validate_chain_id_args(args) + + # sender = cli_shared.prepare_sender(args) + # guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + # sender=sender.address.to_bech32(), + # args=args, + # ) + + # abi = Abi.load(Path(args.abi)) + # chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + # multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + # contract = Address.new_from_bech32(args.contract) + + # receiver = Address.new_from_bech32(args.receiver) + # opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None + # function = args.function if args.function else None + # contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None + # arguments, should_prepare_args = _get_contract_arguments(args) + # token_transfers = _prepare_token_transfers(args.token_transfers) + + # tx = multisig.prepare_async_call_transaction( + # owner=sender, + # nonce=sender.nonce, + # contract=contract, + # receiver=receiver, + # gas_limit=int(args.gas_limit), + # gas_price=int(args.gas_price), + # version=int(args.version), + # options=int(args.options), + # should_prepare_args=should_prepare_args, + # guardian_and_relayer_data=guardian_and_relayer_data, + # native_token_amount=int(args.value), + # token_transfers=token_transfers, + # opt_gas_limit=opt_gas_limit, + # function=function, + # abi=contract_abi, + # arguments=arguments, + # ) + + # _send_or_simulate(tx, contract, args) + + +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("Provide either '--arguments' or '--arguments-file'.") + + if json_args: + if not args.abi: + raise Exception("Can't use '--arguments-file' without providing the Abi file.") + + return json_args, False + else: + return args.arguments, True + + +def _prepare_token_transfers(transfers: list[str]) -> list[TokenTransfer]: + token_computer = TokenComputer() + token_transfers: list[TokenTransfer] = [] + + for i in range(0, len(transfers) - 1, 2): + identifier = transfers[i] + amount = int(transfers[i + 1]) + nonce = token_computer.extract_nonce_from_extended_identifier(identifier) + + token = Token(identifier, nonce) + transfer = TokenTransfer(token, amount) + token_transfers.append(transfer) + + return token_transfers + + +def _send_or_simulate(tx: Transaction, contract_address: Address, args: Any): + output_builder = cli_shared.send_or_simulate(tx, args, dump_output=False) + output_builder.set_contract_address(contract_address) + utils.dump_out_json(output_builder.build(), outfile=args.outfile) diff --git a/multiversx_sdk_cli/multisig.py b/multiversx_sdk_cli/multisig.py new file mode 100644 index 00000000..4964c20e --- /dev/null +++ b/multiversx_sdk_cli/multisig.py @@ -0,0 +1,592 @@ +import logging +from pathlib import Path +from typing import Any, Optional + +from multiversx_sdk import ( + Address, + MultisigTransactionsFactory, + TokenTransfer, + Transaction, + TransactionsFactoryConfig, +) +from multiversx_sdk.abi import ( + Abi, + AddressValue, + BigUIntValue, + BoolValue, + BytesValue, + StringValue, +) + +from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController +from multiversx_sdk_cli.config import get_address_hrp +from multiversx_sdk_cli.contracts import ( + ADDRESS_PREFIX, + FALSE_STR_LOWER, + HEX_PREFIX, + MAINCHAIN_ADDRESS_HRP, + STR_PREFIX, + TRUE_STR_LOWER, +) +from multiversx_sdk_cli.errors import BadUserInput +from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData +from multiversx_sdk_cli.interfaces import IAccount + +logger = logging.getLogger("multisig") + + +class MultisigWrapper(BaseTransactionsController): + def __init__(self, config: TransactionsFactoryConfig, abi: Abi): + self._factory = MultisigTransactionsFactory(config, abi) + + def prepare_deploy_transaction( + self, + owner: IAccount, + nonce: int, + bytecode: Path, + quorum: int, + board_members: list[Address], + upgradeable: bool, + readable: bool, + payable: bool, + payable_by_sc: bool, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_deploy( + sender=owner.address, + bytecode=bytecode, + quorum=quorum, + board=board_members, + gas_limit=gas_limit, + is_upgradeable=upgradeable, + is_readable=readable, + is_payable=payable, + is_payable_by_sc=payable_by_sc, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_deposit_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + native_amount: Optional[int] = None, + token_transfers: Optional[list[TokenTransfer]] = None, + ) -> Transaction: + tx = self._factory.create_transaction_for_deposit( + sender=owner.address, + contract=contract, + gas_limit=gas_limit, + native_token_amount=native_amount, + token_transfers=token_transfers, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_discard_action_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + action_id: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_discard_action( + sender=owner.address, + contract=contract, + action_id=action_id, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_discard_batch_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + action_ids: list[int], + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_discard_batch( + sender=owner.address, + contract=contract, + action_ids=action_ids, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_add_board_member_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + board_member: Address, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_propose_add_board_member( + sender=owner.address, + contract=contract, + board_member=board_member, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_add_proposer_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + proposer: Address, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_propose_add_proposer( + sender=owner.address, + contract=contract, + proposer=proposer, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_remove_user_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + user: Address, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_propose_remove_user( + sender=owner.address, + contract=contract, + user=user, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_change_quorum_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + quorum: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_propose_change_quorum( + sender=owner.address, + contract=contract, + quorum=quorum, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_transfer_execute_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + receiver: Address, + native_token_amount: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + should_prepare_args: bool, + guardian_and_relayer_data: GuardianRelayerData, + opt_gas_limit: Optional[int] = None, + abi: Optional[Abi] = None, + function: Optional[str] = None, + arguments: Optional[list[Any]] = None, + ) -> Transaction: + args = arguments if arguments else [] + if should_prepare_args: + args = self._prepare_args(args) + + tx = self._factory.create_transaction_for_propose_transfer_execute( + sender=owner.address, + contract=contract, + receiver=receiver, + native_token_amount=native_token_amount, + gas_limit=gas_limit, + opt_gas_limit=opt_gas_limit, + abi=abi, + function=function, + arguments=args, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_transfer_execute_esdt_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + receiver: Address, + gas_limit: int, + gas_price: int, + version: int, + options: int, + should_prepare_args: bool, + guardian_and_relayer_data: GuardianRelayerData, + token_transfers: list[TokenTransfer], + opt_gas_limit: Optional[int] = None, + abi: Optional[Abi] = None, + function: Optional[str] = None, + arguments: Optional[list[Any]] = None, + ) -> Transaction: + args = arguments if arguments else [] + if should_prepare_args: + args = self._prepare_args(args) + + tx = self._factory.create_transaction_for_propose_transfer_esdt_execute( + sender=owner.address, + contract=contract, + receiver=receiver, + token_transfers=token_transfers, + gas_limit=gas_limit, + opt_gas_limit=opt_gas_limit, + abi=abi, + function=function, + arguments=args, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_async_call_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + receiver: Address, + gas_limit: int, + gas_price: int, + version: int, + options: int, + should_prepare_args: bool, + guardian_and_relayer_data: GuardianRelayerData, + native_token_amount: int = 0, + token_transfers: Optional[list[TokenTransfer]] = None, + opt_gas_limit: Optional[int] = None, + abi: Optional[Abi] = None, + function: Optional[str] = None, + arguments: Optional[list[Any]] = None, + ) -> Transaction: + args = arguments if arguments else [] + if should_prepare_args: + args = self._prepare_args(args) + + tx = self._factory.create_transaction_for_propose_async_call( + sender=owner.address, + contract=contract, + receiver=receiver, + gas_limit=gas_limit, + native_token_amount=native_token_amount, + token_transfers=token_transfers, + opt_gas_limit=opt_gas_limit, + abi=abi, + function=function, + arguments=args, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_contract_deploy_from_source_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + contract_to_copy: Address, + gas_limit: int, + gas_price: int, + version: int, + options: int, + upgradeable: bool, + readable: bool, + payable: bool, + payable_by_sc: bool, + should_prepare_args: bool, + guardian_and_relayer_data: GuardianRelayerData, + native_token_amount: int = 0, + abi: Optional[Abi] = None, + arguments: Optional[list[Any]] = None, + ) -> Transaction: + args = arguments if arguments else [] + if should_prepare_args: + args = self._prepare_args(args) + + tx = self._factory.create_transaction_for_propose_contract_deploy_from_source( + sender=owner.address, + contract=contract, + gas_limit=gas_limit, + contract_to_copy=contract_to_copy, + is_upgradeable=upgradeable, + is_readable=readable, + is_payable=payable, + is_payable_by_sc=payable_by_sc, + native_token_amount=native_token_amount, + abi=abi, + arguments=args, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def _prepare_args(self, arguments: list[str]) -> list[Any]: + args: list[Any] = [] + + for arg in arguments: + if arg.startswith(HEX_PREFIX): + args.append(BytesValue(self._hex_to_bytes(arg))) + elif arg.isnumeric(): + args.append(BigUIntValue(int(arg))) + elif arg.startswith(ADDRESS_PREFIX): + args.append(AddressValue.new_from_address(Address.new_from_bech32(arg[len(ADDRESS_PREFIX) :]))) + elif arg.startswith(MAINCHAIN_ADDRESS_HRP): + # this flow will be removed in the future + logger.warning( + "Address argument has no prefix. This flow will be removed in the future. Please provide each address using the `addr:` prefix. (e.g. --arguments addr:erd1...)" + ) + args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) + elif arg.startswith(get_address_hrp()): + args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) + elif arg.lower() == FALSE_STR_LOWER: + args.append(BoolValue(False)) + elif arg.lower() == TRUE_STR_LOWER: + args.append(BoolValue(True)) + elif arg.startswith(STR_PREFIX): + args.append(StringValue(arg[len(STR_PREFIX) :])) + else: + raise BadUserInput( + f"Unknown argument type for argument: `{arg}`. Use `mxpy contract --help` to check all supported arguments" + ) + + return args + + def _hex_to_bytes(self, arg: str): + argument = arg[len(HEX_PREFIX) :] + argument = argument.upper() + argument = self.ensure_even_length(argument) + return bytes.fromhex(argument) + + def ensure_even_length(self, string: str) -> str: + if len(string) % 2 == 1: + return "0" + string + return string diff --git a/requirements.txt b/requirements.txt index 7f295a29..4694973a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,5 @@ ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk[ledger]==1.2.0 +# multiversx-sdk[ledger]==1.2.0 +git+https://github.com/multiversx/mx-sdk-py.git@feat/next#egg=multiversx-sdk From 8969b917c8e3ae9be7e6b5afd720748d11d761a6 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 15 May 2025 17:58:58 +0300 Subject: [PATCH 02/64] wip: finished implementation started tests --- multiversx_sdk_cli/cli_multisig.py | 1539 +++++++++++++++-- multiversx_sdk_cli/multisig.py | 383 ++++ multiversx_sdk_cli/tests/test_cli_multisig.py | 308 ++++ 3 files changed, 2047 insertions(+), 183 deletions(-) create mode 100644 multiversx_sdk_cli/tests/test_cli_multisig.py diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index 868f85a2..816054ad 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -6,6 +6,9 @@ from multiversx_sdk import ( Address, AddressComputer, + MultisigController, + ProxyNetworkProvider, + SmartContractController, Token, TokenComputer, TokenTransfer, @@ -19,6 +22,7 @@ ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, + validate_proxy_argument, validate_transaction_args, ) from multiversx_sdk_cli.cli_output import CLIOutputBuilder @@ -452,8 +456,10 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: _add_contract_arg(sub) _add_abi_arg(sub) - sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") + sub.add_argument("--contract-to-copy", required=True, type=str, help="the bech32 address of the contract to copy") + sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to copy") _add_arguments_arg(sub) + _add_metadata_arg(sub) cli_shared.add_outfile_arg(sub) cli_shared.add_wallet_args(args, sub) cli_shared.add_proxy_arg(sub) @@ -476,77 +482,1061 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.set_defaults(func=deploy_from_source) - parser.epilog = cli_shared.build_group_epilog(subparsers) - return subparsers + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "upgrade-from-source", + f"Propose a smart contract upgrade from a previously deployed smart contract.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + sub.add_argument( + "--contract-to-upgrade", required=True, type=str, help="the bech32 address of the contract to upgrade" + ) + sub.add_argument("--contract-to-copy", required=True, type=str, help="the bech32 address of the contract to copy") + sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to copy") + _add_arguments_arg(sub) + _add_metadata_arg(sub) + 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) -def _add_bytecode_arg(sub: Any): sub.add_argument( - "--bytecode", - type=str, - required=True, - help="the file containing the WASM bytecode", + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + sub.set_defaults(func=upgrade_from_source) -def _add_contract_arg(sub: Any): - sub.add_argument("--contract", required=True, type=str, help="🖄 the bech32 address of the Multisig Smart Contract") + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "sign-action", + f"Sign a proposed action.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + _add_action_id_arg(sub) + 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) -def _add_abi_arg(sub: Any): - sub.add_argument("--abi", required=True, type=str, help="the ABI file of the Multisig Smart Contract") + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + sub.set_defaults(func=sign_action) -def _add_action_id_arg(sub: Any): - sub.add_argument("--action", required=True, type=int, help="the id of the action") + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "sign-batch", + f"Sign a batch of actions.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + sub.add_argument("--batch", required=True, type=int, help="the id of the batch to sign") + 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) -def _add_metadata_arg(sub: Any): sub.add_argument( - "--metadata-not-upgradeable", - dest="metadata_upgradeable", - action="store_false", - help="‼ mark the contract as NOT upgradeable (default: upgradeable)", + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", ) sub.add_argument( - "--metadata-not-readable", - dest="metadata_readable", - action="store_false", - help="‼ mark the contract as NOT readable (default: readable)", + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=sign_batch) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "sign-and-perform", + f"Sign a proposed action and perform it. Works only if quorum is reached.{output_description}", ) + _add_contract_arg(sub) + _add_abi_arg(sub) + + _add_action_id_arg(sub) + 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) + sub.add_argument( - "--metadata-payable", - dest="metadata_payable", + "--wait-result", action="store_true", - help="‼ mark the contract as payable (default: not payable)", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", ) sub.add_argument( - "--metadata-payable-by-sc", - dest="metadata_payable_by_sc", + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=sign_and_perform) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "sign-batch-and-perform", + f"Sign a batch of actions and perform them. Works only if quorum is reached.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + + sub.add_argument("--batch", required=True, type=int, help="the id of the batch to sign") + 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) + + sub.add_argument( + "--wait-result", action="store_true", - help="‼ mark the contract as payable by SC (default: not payable by SC)", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", ) - sub.set_defaults(metadata_upgradeable=True, metadata_payable=False) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + sub.set_defaults(func=sign_batch_and_perform) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "unsign-action", + f"Unsign a proposed action.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + + _add_action_id_arg(sub) + 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) -def _add_arguments_arg(sub: Any): sub.add_argument( - "--arguments", + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=unsign_action) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "unsign-batch", + f"Unsign a batch of actions.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + + sub.add_argument("--batch", required=True, type=int, help="the id of the batch to sign") + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=unsign_batch) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "unsign-for-outdated-members", + f"Unsign an action for outdated board members.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + + _add_action_id_arg(sub) + sub.add_argument( + "--outdated-members", 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 addr:erd1[..]", + type=int, + help="IDs of the outdated board members", + ) + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", ) sub.add_argument( - "--arguments-file", - type=str, - help="a json file containing the arguments. ONLY if abi file is provided. " - "E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }]", + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=unsign_for_outdated_board_members) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "perform-action", + f"Perform an action that has reached quorum.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + + _add_action_id_arg(sub) + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=perform_action) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "perform-batch", + f"Perform a batch of actions that has reached quorum.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + + sub.add_argument("--batch", required=True, type=int, help="the id of the batch to sign") + 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) + + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + + sub.set_defaults(func=perform_batch) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-quorum", + f"Perform a smart contract query to get the quorum.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_quorum) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-num-board-members", + f"Perform a smart contract query to get the number of board members.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_num_board_members) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-num-groups", + f"Perform a smart contract query to get the number of groups.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_num_groups) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-num-proposers", + f"Perform a smart contract query to get the number of proposers.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_num_proposers) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-action-group", + f"Perform a smart contract query to get the actions in a group.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + sub.add_argument("--group", required=True, type=int, help="the group id") + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_action_group) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-last-action-group-id", + f"Perform a smart contract query to get the id of the last action in a group.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_last_group_action_id) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-action-last-index", + f"Perform a smart contract query to get the index of the last action.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_action_last_index) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "is-signed-by", + f"Perform a smart contract query to check if an action is signed by a user.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + _add_action_id_arg(sub) + sub.add_argument("--user", required=True, type=str, help="the bech32 address of the user") + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=is_signed_by) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "is-quorum-reached", + f"Perform a smart contract query to check if an action has reached quorum.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + _add_action_id_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=is_quorum_reached) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-pending-actions", + f"Perform a smart contract query to get the pending actions full info.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_pending_actions_full_info) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-user-role", + f"Perform a smart contract query to get the role of a user.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + sub.add_argument("--user", required=True, type=str, help="the bech32 address of the user") + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_user_role) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-board-members", + f"Perform a smart contract query to get all the board members.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_all_board_members) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-proposers", + f"Perform a smart contract query to get all the proposers.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_all_proposers) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-action-data", + f"Perform a smart contract query to get the data of an action.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + _add_action_id_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_action_data) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-action-signers", + f"Perform a smart contract query to get the signers of an action.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + _add_action_id_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-action-signers-count", + f"Perform a smart contract query to get the number of signers of an action.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + _add_action_id_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_action_signer_count) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-action-valid-signers-count", + f"Perform a smart contract query to get the number of valid signers of an action.{output_description}", + ) + _add_contract_arg(sub) + _add_abi_arg(sub) + _add_action_id_arg(sub) + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_action_valid_signer_count) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "parse-propose-action", + f"Parses the propose action transaction to extract proposal ID.{output_description}", + ) + _add_abi_arg(sub) + sub.add_argument("--hash", required=True, type=str, help="the transaction hash of the propose action") + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=parse_proposal) + + parser.epilog = cli_shared.build_group_epilog(subparsers) + return subparsers + + +def _add_bytecode_arg(sub: Any): + sub.add_argument( + "--bytecode", + type=str, + required=True, + help="the file containing the WASM bytecode", + ) + + +def _add_contract_arg(sub: Any): + sub.add_argument("--contract", required=True, type=str, help="🖄 the bech32 address of the Multisig Smart Contract") + + +def _add_abi_arg(sub: Any): + sub.add_argument("--abi", required=True, type=str, help="the ABI file of the Multisig Smart Contract") + + +def _add_action_id_arg(sub: Any): + sub.add_argument("--action", required=True, type=int, help="the id of the action") + + +def _add_metadata_arg(sub: Any): + sub.add_argument( + "--metadata-not-upgradeable", + dest="metadata_upgradeable", + action="store_false", + help="‼ mark the contract as NOT upgradeable (default: upgradeable)", + ) + sub.add_argument( + "--metadata-not-readable", + dest="metadata_readable", + action="store_false", + help="‼ mark the contract as NOT readable (default: readable)", + ) + sub.add_argument( + "--metadata-payable", + dest="metadata_payable", + action="store_true", + help="‼ mark the contract as payable (default: not payable)", + ) + sub.add_argument( + "--metadata-payable-by-sc", + dest="metadata_payable_by_sc", + action="store_true", + help="‼ mark the contract as payable by SC (default: not payable by SC)", + ) + sub.set_defaults(metadata_upgradeable=True, metadata_payable=False) + + +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 addr:erd1[..]", + ) + sub.add_argument( + "--arguments-file", + type=str, + help="a json file containing the arguments. ONLY if abi file is provided. " + "E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }]", + ) + + +def deploy(args: Any): + logger.debug("multisig.deploy") + + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + quorum = args.quorum + board_members = [Address.new_from_bech32(addr) for addr in args.board_members] + + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + tx = multisig.prepare_deploy_transaction( + owner=sender, + nonce=sender.nonce, + bytecode=Path(args.bytecode), + quorum=quorum, + board_members=board_members, + upgradeable=args.metadata_upgradeable, + readable=args.metadata_readable, + payable=args.metadata_payable, + payable_by_sc=args.metadata_payable_by_sc, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + address_computer = AddressComputer(NUMBER_OF_SHARDS) + contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=tx.nonce) + + logger.info("Contract address: %s", contract_address.to_bech32()) + utils.log_explorer_contract_address(args.chain, contract_address.to_bech32()) + + _send_or_simulate(tx, contract_address, args) + + +def deposit(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + native_amount = int(args.value) + + token_transfers = args.token_transfers or None + if token_transfers: + token_transfers = _prepare_token_transfers(token_transfers) + + tx = multisig.prepare_deposit_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + native_amount=native_amount, + token_transfers=token_transfers, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def discard_action(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) + + tx = multisig.prepare_discard_action_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + action_id=action_id, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def discard_batch(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + actions = args.action_ids + + tx = multisig.prepare_discard_batch_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + action_ids=actions, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def add_board_member(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + board_member = Address.new_from_bech32(args.board_member) + + tx = multisig.prepare_add_board_member_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + board_member=board_member, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def add_proposer(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + proposer = Address.new_from_bech32(args.proposer) + + tx = multisig.prepare_add_proposer_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + proposer=proposer, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def remove_user(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + user = Address.new_from_bech32(args.user) + + tx = multisig.prepare_remove_user_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + user=user, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def change_quorum(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + quorum = int(args.quorum) + + tx = multisig.prepare_change_quorum_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + quorum=quorum, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def transfer_and_execute(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + + receiver = Address.new_from_bech32(args.receiver) + opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None + function = args.function if args.function else None + contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None + arguments, should_prepare_args = _get_contract_arguments(args) + + tx = multisig.prepare_transfer_execute_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + receiver=receiver, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + should_prepare_args=should_prepare_args, + guardian_and_relayer_data=guardian_and_relayer_data, + opt_gas_limit=opt_gas_limit, + function=function, + abi=contract_abi, + arguments=arguments, + native_token_amount=int(args.value), + ) + + _send_or_simulate(tx, contract, args) + + +def transfer_and_execute_esdt(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + if int(args.value) != 0: + raise Exception("Native token transfer is not allowed for this command.") + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + + receiver = Address.new_from_bech32(args.receiver) + opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None + function = args.function if args.function else None + contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None + arguments, should_prepare_args = _get_contract_arguments(args) + token_transfers = _prepare_token_transfers(args.token_transfers) + + tx = multisig.prepare_transfer_execute_esdt_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + receiver=receiver, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + should_prepare_args=should_prepare_args, + guardian_and_relayer_data=guardian_and_relayer_data, + token_transfers=token_transfers, + opt_gas_limit=opt_gas_limit, + function=function, + abi=contract_abi, + arguments=arguments, + ) + + _send_or_simulate(tx, contract, args) + + +def async_call(args: Any): + validate_transaction_args(args) + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + + receiver = Address.new_from_bech32(args.receiver) + opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None + function = args.function if args.function else None + contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None + arguments, should_prepare_args = _get_contract_arguments(args) + token_transfers = _prepare_token_transfers(args.token_transfers) + + tx = multisig.prepare_async_call_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + receiver=receiver, + gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), + version=int(args.version), + options=int(args.options), + should_prepare_args=should_prepare_args, + guardian_and_relayer_data=guardian_and_relayer_data, + native_token_amount=int(args.value), + token_transfers=token_transfers, + opt_gas_limit=opt_gas_limit, + function=function, + abi=contract_abi, + arguments=arguments, ) + _send_or_simulate(tx, contract, args) -def deploy(args: Any): - logger.debug("multisig.deploy") +def deploy_from_source(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -559,39 +1549,39 @@ def deploy(args: Any): ) abi = Abi.load(Path(args.abi)) - quorum = args.quorum - board_members = [Address.new_from_bech32(addr) for addr in args.board_members] - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) - tx = multisig.prepare_deploy_transaction( + contract = Address.new_from_bech32(args.contract) + contract_to_copy = Address.new_from_bech32(args.contract_to_copy) + + contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None + arguments, should_prepare_args = _get_contract_arguments(args) + + tx = multisig.prepare_contract_deploy_from_source_transaction( owner=sender, nonce=sender.nonce, - bytecode=Path(args.bytecode), - quorum=quorum, - board_members=board_members, - upgradeable=args.metadata_upgradeable, - readable=args.metadata_readable, - payable=args.metadata_payable, - payable_by_sc=args.metadata_payable_by_sc, + contract=contract, + contract_to_copy=contract_to_copy, gas_limit=int(args.gas_limit), gas_price=int(args.gas_price), version=int(args.version), options=int(args.options), + upgradeable=args.metadata_upgradeable, + readable=args.metadata_readable, + payable=args.metadata_payable, + payable_by_sc=args.metadata_payable_by_sc, + should_prepare_args=should_prepare_args, guardian_and_relayer_data=guardian_and_relayer_data, + native_token_amount=int(args.value), + abi=contract_abi, + arguments=arguments, ) - address_computer = AddressComputer(NUMBER_OF_SHARDS) - contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=tx.nonce) - - logger.info("Contract address: %s", contract_address.to_bech32()) - utils.log_explorer_contract_address(args.chain, contract_address.to_bech32()) - - _send_or_simulate(tx, contract_address, args) + _send_or_simulate(tx, contract, args) -def deposit(args: Any): +def upgrade_from_source(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -608,26 +1598,37 @@ def deposit(args: Any): multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) contract = Address.new_from_bech32(args.contract) - native_amount = int(args.value) - token_transfers = _prepare_token_transfers(args.token_transfers) + contract_to_upgrade = Address.new_from_bech32(args.contract_to_upgrade) + contract_to_copy = Address.new_from_bech32(args.contract_to_copy) - tx = multisig.prepare_deposit_transaction( + contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None + arguments, should_prepare_args = _get_contract_arguments(args) + + tx = multisig.prepare_contract_upgrade_from_source_transaction( owner=sender, nonce=sender.nonce, contract=contract, - native_amount=native_amount, - token_transfers=token_transfers, + contract_to_upgrade=contract_to_upgrade, + contract_to_copy=contract_to_copy, gas_limit=int(args.gas_limit), gas_price=int(args.gas_price), version=int(args.version), options=int(args.options), + upgradeable=args.metadata_upgradeable, + readable=args.metadata_readable, + payable=args.metadata_payable, + payable_by_sc=args.metadata_payable_by_sc, + should_prepare_args=should_prepare_args, guardian_and_relayer_data=guardian_and_relayer_data, + native_token_amount=int(args.value), + abi=contract_abi, + arguments=arguments, ) _send_or_simulate(tx, contract, args) -def discard_action(args: Any): +def sign_action(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -646,7 +1647,7 @@ def discard_action(args: Any): contract = Address.new_from_bech32(args.contract) action_id = int(args.action) - tx = multisig.prepare_discard_action_transaction( + tx = multisig.prepare_sign_action_transaction( owner=sender, nonce=sender.nonce, contract=contract, @@ -661,7 +1662,7 @@ def discard_action(args: Any): _send_or_simulate(tx, contract, args) -def discard_batch(args: Any): +def sign_batch(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -678,13 +1679,13 @@ def discard_batch(args: Any): multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) contract = Address.new_from_bech32(args.contract) - actions = args.action_ids + batch_id = int(args.batch) - tx = multisig.prepare_discard_batch_transaction( + tx = multisig.prepare_sign_batch_transaction( owner=sender, nonce=sender.nonce, contract=contract, - action_ids=actions, + batch_id=batch_id, gas_limit=int(args.gas_limit), gas_price=int(args.gas_price), version=int(args.version), @@ -695,7 +1696,7 @@ def discard_batch(args: Any): _send_or_simulate(tx, contract, args) -def add_board_member(args: Any): +def sign_and_perform(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -712,13 +1713,13 @@ def add_board_member(args: Any): multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) contract = Address.new_from_bech32(args.contract) - board_member = Address.new_from_bech32(args.board_member) + action_id = int(args.action) - tx = multisig.prepare_add_board_member_transaction( + tx = multisig.prepare_sign_and_perform_transaction( owner=sender, nonce=sender.nonce, contract=contract, - board_member=board_member, + action_id=action_id, gas_limit=int(args.gas_limit), gas_price=int(args.gas_price), version=int(args.version), @@ -729,7 +1730,7 @@ def add_board_member(args: Any): _send_or_simulate(tx, contract, args) -def add_proposer(args: Any): +def sign_batch_and_perform(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -746,13 +1747,13 @@ def add_proposer(args: Any): multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) contract = Address.new_from_bech32(args.contract) - proposer = Address.new_from_bech32(args.proposer) + batch_id = int(args.batch) - tx = multisig.prepare_add_proposer_transaction( + tx = multisig.prepare_sign_batch_and_perform_transaction( owner=sender, nonce=sender.nonce, contract=contract, - proposer=proposer, + batch_id=batch_id, gas_limit=int(args.gas_limit), gas_price=int(args.gas_price), version=int(args.version), @@ -763,7 +1764,7 @@ def add_proposer(args: Any): _send_or_simulate(tx, contract, args) -def remove_user(args: Any): +def unsign_action(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -780,13 +1781,13 @@ def remove_user(args: Any): multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) contract = Address.new_from_bech32(args.contract) - user = Address.new_from_bech32(args.user) + action_id = int(args.action) - tx = multisig.prepare_remove_user_transaction( + tx = multisig.prepare_unsign_action_transaction( owner=sender, nonce=sender.nonce, contract=contract, - user=user, + action_id=action_id, gas_limit=int(args.gas_limit), gas_price=int(args.gas_price), version=int(args.version), @@ -797,7 +1798,7 @@ def remove_user(args: Any): _send_or_simulate(tx, contract, args) -def change_quorum(args: Any): +def unsign_batch(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -814,13 +1815,13 @@ def change_quorum(args: Any): multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) contract = Address.new_from_bech32(args.contract) - quorum = int(args.quorum) + batch_id = int(args.batch) - tx = multisig.prepare_change_quorum_transaction( + tx = multisig.prepare_unsign_batch_transaction( owner=sender, nonce=sender.nonce, contract=contract, - quorum=quorum, + batch_id=batch_id, gas_limit=int(args.gas_limit), gas_price=int(args.gas_price), version=int(args.version), @@ -831,7 +1832,7 @@ def change_quorum(args: Any): _send_or_simulate(tx, contract, args) -def transfer_and_execute(args: Any): +def unsign_for_outdated_board_members(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -848,43 +1849,30 @@ def transfer_and_execute(args: Any): multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) - receiver = Address.new_from_bech32(args.receiver) - opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None - function = args.function if args.function else None - contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None - arguments, should_prepare_args = _get_contract_arguments(args) - - tx = multisig.prepare_transfer_execute_transaction( + tx = multisig.prepare_unsign_for_outdated_board_members_transaction( owner=sender, nonce=sender.nonce, contract=contract, - receiver=receiver, + action_id=action_id, + outdated_board_members=args.outdated_members, gas_limit=int(args.gas_limit), gas_price=int(args.gas_price), version=int(args.version), options=int(args.options), - should_prepare_args=should_prepare_args, guardian_and_relayer_data=guardian_and_relayer_data, - opt_gas_limit=opt_gas_limit, - function=function, - abi=contract_abi, - arguments=arguments, - native_token_amount=int(args.value), ) _send_or_simulate(tx, contract, args) -def transfer_and_execute_esdt(args: Any): +def perform_action(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) - if int(args.value) != 0: - raise Exception("Native token transfer is not allowed for this command.") - sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -896,36 +1884,24 @@ def transfer_and_execute_esdt(args: Any): multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) - receiver = Address.new_from_bech32(args.receiver) - opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None - function = args.function if args.function else None - contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None - arguments, should_prepare_args = _get_contract_arguments(args) - token_transfers = _prepare_token_transfers(args.token_transfers) - - tx = multisig.prepare_transfer_execute_esdt_transaction( + tx = multisig.prepare_perform_action_transaction( owner=sender, nonce=sender.nonce, contract=contract, - receiver=receiver, + action_id=action_id, gas_limit=int(args.gas_limit), gas_price=int(args.gas_price), version=int(args.version), options=int(args.options), - should_prepare_args=should_prepare_args, guardian_and_relayer_data=guardian_and_relayer_data, - token_transfers=token_transfers, - opt_gas_limit=opt_gas_limit, - function=function, - abi=contract_abi, - arguments=arguments, ) _send_or_simulate(tx, contract, args) -def async_call(args: Any): +def perform_batch(args: Any): validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -942,82 +1918,279 @@ def async_call(args: Any): multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) contract = Address.new_from_bech32(args.contract) + batch_id = int(args.batch) - receiver = Address.new_from_bech32(args.receiver) - opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None - function = args.function if args.function else None - contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None - arguments, should_prepare_args = _get_contract_arguments(args) - token_transfers = _prepare_token_transfers(args.token_transfers) - - tx = multisig.prepare_async_call_transaction( + tx = multisig.prepare_perform_batch_transaction( owner=sender, nonce=sender.nonce, contract=contract, - receiver=receiver, + batch_id=batch_id, gas_limit=int(args.gas_limit), gas_price=int(args.gas_price), version=int(args.version), options=int(args.options), - should_prepare_args=should_prepare_args, guardian_and_relayer_data=guardian_and_relayer_data, - native_token_amount=int(args.value), - token_transfers=token_transfers, - opt_gas_limit=opt_gas_limit, - function=function, - abi=contract_abi, - arguments=arguments, ) _send_or_simulate(tx, contract, args) -def deploy_from_source(args: Any): - pass - # validate_transaction_args(args) - # ensure_wallet_args_are_provided(args) - # validate_broadcast_args(args) - # validate_chain_id_args(args) - - # sender = cli_shared.prepare_sender(args) - # guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( - # sender=sender.address.to_bech32(), - # args=args, - # ) - - # abi = Abi.load(Path(args.abi)) - # chain_id = cli_shared.get_chain_id(args.chain, args.proxy) - # multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) - - # contract = Address.new_from_bech32(args.contract) - - # receiver = Address.new_from_bech32(args.receiver) - # opt_gas_limit = int(args.opt_gas_limit) if args.opt_gas_limit else None - # function = args.function if args.function else None - # contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None - # arguments, should_prepare_args = _get_contract_arguments(args) - # token_transfers = _prepare_token_transfers(args.token_transfers) - - # tx = multisig.prepare_async_call_transaction( - # owner=sender, - # nonce=sender.nonce, - # contract=contract, - # receiver=receiver, - # gas_limit=int(args.gas_limit), - # gas_price=int(args.gas_price), - # version=int(args.version), - # options=int(args.options), - # should_prepare_args=should_prepare_args, - # guardian_and_relayer_data=guardian_and_relayer_data, - # native_token_amount=int(args.value), - # token_transfers=token_transfers, - # opt_gas_limit=opt_gas_limit, - # function=function, - # abi=contract_abi, - # arguments=arguments, - # ) - - # _send_or_simulate(tx, contract, args) +def get_quorum(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + quorum = multisig.get_quorum(Address.new_from_bech32(args.contract)) + print(f"Quorum: {quorum}") + + +def get_num_board_members(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + num_board_members = multisig.get_num_board_members(Address.new_from_bech32(args.contract)) + print(f"Number of board members: {num_board_members}") + + +def get_num_groups(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + num_groups = multisig.get_num_groups(Address.new_from_bech32(args.contract)) + print(f"Number of groups: {num_groups}") + + +def get_num_proposers(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + num_proposers = multisig.get_num_proposers(Address.new_from_bech32(args.contract)) + print(f"Number of proposers: {num_proposers}") + + +def get_action_group(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + actions = multisig.get_action_group(Address.new_from_bech32(args.contract), args.group) + print(f"Actions: [{actions}]") + + +def get_last_group_action_id(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + id = multisig.get_last_group_action_id(Address.new_from_bech32(args.contract)) + print(f"Last group action id: {id}") + + +def get_action_last_index(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + id = multisig.get_action_last_index(Address.new_from_bech32(args.contract)) + print(f"Action last index: {id}") + + +def is_signed_by(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) + user = Address.new_from_bech32(args.user) + + is_signed = multisig.is_signed_by(contract, user, action_id) + print(f"Action {action_id} is signed by {user.to_bech32()}: {is_signed}") + + +def is_quorum_reached(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) + + is_quorum_reached = multisig.is_quorum_reached(contract, action_id) + print(f"Quorum reached for action {action_id}: {is_quorum_reached}") + + +def get_pending_actions_full_info(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + controller = SmartContractController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + [values] = controller.query( + contract=contract, + function="getPendingActionFullInfo", + arguments=[None], + ) + + print(values) + + +def get_user_role(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + user = Address.new_from_bech32(args.user) + + role = multisig.get_user_role(contract, user) + print(f"User {user.to_bech32()} has role: {role.name}") + + +def get_all_board_members(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + + board_members = multisig.get_all_board_members(contract) + print("Board members:") + for member in board_members: + print(f" - {member.to_bech32()}") + + +def get_all_proposers(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + + proposers = multisig.get_all_proposers(contract) + print("Proposers:") + for proposer in proposers: + print(f" - {proposer.to_bech32()}") + + +def get_action_data(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + controller = SmartContractController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + action = args.action + + [value] = controller.query( + contract=contract, + function="getActionData", + arguments=[action], + ) + print(value) + + +def get_action_signers(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + action = args.action + + signers = multisig.get_action_signers(contract, action) + print(f"Signers for action {action}:") + for signer in signers: + print(f" - {signer.to_bech32()}") + + +def get_action_signer_count(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + action = args.action + + signers = multisig.get_action_signer_count(contract, action) + print(f"{signers} signers for action {action}:") + + +def get_action_valid_signer_count(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + action = args.action + + signers = multisig.get_action_valid_signer_count(contract, action) + print(f"{signers} valid signers for action {action}:") + + +def parse_proposal(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + proxy = ProxyNetworkProvider(args.proxy) + chain_id = proxy.get_network_config().chain_id + multisig = MultisigController(chain_id, proxy, abi) + + id = multisig.await_completed_execute_propose_any(args.hash) + print(f"Proposal ID: {id}") def _get_contract_arguments(args: Any) -> tuple[list[Any], bool]: diff --git a/multiversx_sdk_cli/multisig.py b/multiversx_sdk_cli/multisig.py index 4964c20e..200e0860 100644 --- a/multiversx_sdk_cli/multisig.py +++ b/multiversx_sdk_cli/multisig.py @@ -549,6 +549,389 @@ def prepare_contract_deploy_from_source_transaction( return tx + def prepare_contract_upgrade_from_source_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + contract_to_upgrade: Address, + contract_to_copy: Address, + gas_limit: int, + gas_price: int, + version: int, + options: int, + upgradeable: bool, + readable: bool, + payable: bool, + payable_by_sc: bool, + should_prepare_args: bool, + guardian_and_relayer_data: GuardianRelayerData, + native_token_amount: int = 0, + abi: Optional[Abi] = None, + arguments: Optional[list[Any]] = None, + ) -> Transaction: + args = arguments if arguments else [] + if should_prepare_args: + args = self._prepare_args(args) + + tx = self._factory.create_transaction_for_propose_contract_upgrade_from_source( + sender=owner.address, + contract=contract, + contract_to_upgrade=contract_to_upgrade, + contract_to_copy=contract_to_copy, + gas_limit=gas_limit, + is_upgradeable=upgradeable, + is_readable=readable, + is_payable=payable, + is_payable_by_sc=payable_by_sc, + native_token_amount=native_token_amount, + abi=abi, + arguments=args, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_sign_action_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + action_id: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_sign_action( + sender=owner.address, + contract=contract, + action_id=action_id, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_sign_batch_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + batch_id: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_sign_batch( + sender=owner.address, + contract=contract, + batch_id=batch_id, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_sign_and_perform_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + action_id: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_sign_and_perform( + sender=owner.address, + contract=contract, + action_id=action_id, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_sign_batch_and_perform_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + batch_id: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_sign_batch_and_perform( + sender=owner.address, + contract=contract, + batch_id=batch_id, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_unsign_action_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + action_id: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_unsign_action( + sender=owner.address, + contract=contract, + action_id=action_id, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_unsign_batch_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + batch_id: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_unsign_batch( + sender=owner.address, + contract=contract, + batch_id=batch_id, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_unsign_for_outdated_board_members_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + action_id: int, + outdated_board_members: list[int], + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_unsign_for_outdated_board_members( + sender=owner.address, + contract=contract, + action_id=action_id, + outdated_board_members=outdated_board_members, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_perform_action_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + action_id: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_perform_action( + sender=owner.address, + contract=contract, + action_id=action_id, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def prepare_perform_batch_transaction( + self, + owner: IAccount, + nonce: int, + contract: Address, + batch_id: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self._factory.create_transaction_for_perform_batch( + sender=owner.address, + contract=contract, + batch_id=batch_id, + gas_limit=gas_limit, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.sign_transaction( + transaction=tx, + sender=owner, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + def _prepare_args(self, arguments: list[str]) -> list[Any]: args: list[Any] = [] diff --git a/multiversx_sdk_cli/tests/test_cli_multisig.py b/multiversx_sdk_cli/tests/test_cli_multisig.py new file mode 100644 index 00000000..7f2bd0c1 --- /dev/null +++ b/multiversx_sdk_cli/tests/test_cli_multisig.py @@ -0,0 +1,308 @@ +import base64 +import json +from pathlib import Path +from typing import Any + +from multiversx_sdk_cli.cli import main + +testdata = Path(__file__).parent / "testdata" +user = testdata / "testUser.pem" +user_address = "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" +multisig_abi = testdata / "multisig.abi.json" +contract_address = "erd1qqqqqqqqqqqqqpgqe832k3l6d02ww7l9cvqum25539nmmdxa9ncsdutjuf" +contract_address_hex = "00000000000000000500c9e2ab47fa6bd4e77be5c301cdaa948967bdb4dd2cf1" +bob = "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" + + +def test_deploy_multisig(capsys: Any): + multisig_bytecode = (testdata / "multisig.wasm").read_bytes() + + return_code = main( + [ + "multisig", + "deploy", + "--bytecode", + str(testdata / "multisig.wasm"), + "--abi", + str(multisig_abi), + "--quorum", + "1", + "--board-members", + user_address, + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "100000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu" + assert tx["value"] == "0" + assert tx["gasLimit"] == 100_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert f"{multisig_bytecode.hex()}@0500@0504@02@c0006edaaee4fd479f2f248b341eb11eaecaec4d7dee190619958332bba5200f" + + +def test_deposit_native_token(capsys: Any): + return_code = main( + [ + "multisig", + "deposit", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + "--value", + "1000000000000000000", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "1000000000000000000" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "deposit" + + +def test_deposit_esdt(capsys: Any): + return_code = main( + [ + "multisig", + "deposit", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + "--token-transfers", + "MYTKN-a584f9", + "100000", + "SFT-1bc261-01", + "1", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == user_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert ( + data + == f"MultiESDTNFTTransfer@{contract_address_hex}@02@4d59544b4e2d613538346639@@0186a0@5346542d316263323631@01@01@6465706f736974" + ) + + +def test_discard_action(capsys: Any): + return_code = main( + [ + "multisig", + "discard-action", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--action", + "7", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "discardAction@07" + + +def test_discard_batch(capsys: Any): + return_code = main( + [ + "multisig", + "discard-batch", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--action-ids", + "7", + "8", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "discardBatch@07@08" + + +def test_add_board_member(capsys: Any): + return_code = main( + [ + "multisig", + "add-board-member", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--board-member", + bob, + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "proposeAddBoardMember@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8" + + +def test_add_proposer(capsys: Any): + return_code = main( + [ + "multisig", + "add-proposer", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--proposer", + bob, + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "proposeAddProposer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8" + + +def test_remove_user(capsys: Any): + return_code = main( + [ + "multisig", + "remove-user", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--user", + bob, + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "proposeRemoveUser@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8" + + +def _read_stdout(capsys: Any) -> str: + stdout: str = capsys.readouterr().out.strip() + return stdout + + +def get_transaction(capsys: Any) -> dict[str, Any]: + out = _read_stdout(capsys) + output: dict[str, Any] = json.loads(out) + return output["emittedTransaction"] From f69d08549e1fe72f1f8c164fcd89352d95e21154 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 16 May 2025 17:01:01 +0300 Subject: [PATCH 03/64] tests and fixes --- multiversx_sdk_cli/cli_multisig.py | 104 +-- multiversx_sdk_cli/tests/test_cli_multisig.py | 650 ++++++++++++++++++ 2 files changed, 715 insertions(+), 39 deletions(-) diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index 816054ad..3bdda019 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -8,7 +8,6 @@ AddressComputer, MultisigController, ProxyNetworkProvider, - SmartContractController, Token, TokenComputer, TokenTransfer, @@ -26,6 +25,7 @@ validate_transaction_args, ) from multiversx_sdk_cli.cli_output import CLIOutputBuilder +from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS from multiversx_sdk_cli.multisig import MultisigWrapper @@ -999,6 +999,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: _add_action_id_arg(sub) cli_shared.add_proxy_arg(sub) + sub.set_defaults(func=get_action_signers) + sub = cli_shared.add_command_subparser( subparsers, "multisig", @@ -1449,6 +1451,9 @@ def transfer_and_execute_esdt(args: Any): if int(args.value) != 0: raise Exception("Native token transfer is not allowed for this command.") + if not args.token_transfers: + raise Exception("Token transfers not provided.") + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -1512,7 +1517,10 @@ def async_call(args: Any): function = args.function if args.function else None contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None arguments, should_prepare_args = _get_contract_arguments(args) - token_transfers = _prepare_token_transfers(args.token_transfers) + + token_transfers = args.token_transfers or None + if token_transfers: + token_transfers = _prepare_token_transfers(args.token_transfers) tx = multisig.prepare_async_call_transaction( owner=sender, @@ -1939,7 +1947,8 @@ def get_quorum(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -1951,7 +1960,8 @@ def get_num_board_members(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -1963,7 +1973,8 @@ def get_num_groups(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -1975,7 +1986,8 @@ def get_num_proposers(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -1987,7 +1999,8 @@ def get_action_group(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -1999,7 +2012,8 @@ def get_last_group_action_id(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -2011,7 +2025,8 @@ def get_action_last_index(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -2023,7 +2038,8 @@ def is_signed_by(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -2039,7 +2055,8 @@ def is_quorum_reached(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -2051,19 +2068,17 @@ def is_quorum_reached(args: Any): def get_pending_actions_full_info(args: Any): + # TODO: needs fix validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id - controller = SmartContractController(chain_id, proxy, abi) - contract = Address.new_from_bech32(args.contract) - [values] = controller.query( - contract=contract, - function="getPendingActionFullInfo", - arguments=[None], - ) + + controller = MultisigController(chain_id, proxy, abi) + values = controller.get_pending_actions_full_info(contract) print(values) @@ -2072,7 +2087,8 @@ def get_user_role(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -2087,50 +2103,56 @@ def get_all_board_members(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) contract = Address.new_from_bech32(args.contract) board_members = multisig.get_all_board_members(contract) - print("Board members:") - for member in board_members: - print(f" - {member.to_bech32()}") + if not board_members: + print(None) + else: + print("Board members:") + for member in board_members: + print(f" - {member.to_bech32()}") def get_all_proposers(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) contract = Address.new_from_bech32(args.contract) proposers = multisig.get_all_proposers(contract) - print("Proposers:") - for proposer in proposers: - print(f" - {proposer.to_bech32()}") + if not proposers: + print(None) + else: + print("Proposers:") + for proposer in proposers: + print(f" - {proposer.to_bech32()}") def get_action_data(args: Any): + # TODO: needs fix validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id - controller = SmartContractController(chain_id, proxy, abi) + controller = MultisigController(chain_id, proxy, abi) contract = Address.new_from_bech32(args.contract) action = args.action - [value] = controller.query( - contract=contract, - function="getActionData", - arguments=[action], - ) + value = controller.get_action_data(contract=contract, action_id=action) print(value) @@ -2138,7 +2160,8 @@ def get_action_signers(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -2155,7 +2178,8 @@ def get_action_signer_count(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -2170,7 +2194,8 @@ def get_action_valid_signer_count(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) @@ -2185,7 +2210,8 @@ def parse_proposal(args: Any): validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) chain_id = proxy.get_network_config().chain_id multisig = MultisigController(chain_id, proxy, abi) diff --git a/multiversx_sdk_cli/tests/test_cli_multisig.py b/multiversx_sdk_cli/tests/test_cli_multisig.py index 7f2bd0c1..e280e867 100644 --- a/multiversx_sdk_cli/tests/test_cli_multisig.py +++ b/multiversx_sdk_cli/tests/test_cli_multisig.py @@ -12,6 +12,7 @@ contract_address = "erd1qqqqqqqqqqqqqpgqe832k3l6d02ww7l9cvqum25539nmmdxa9ncsdutjuf" contract_address_hex = "00000000000000000500c9e2ab47fa6bd4e77be5c301cdaa948967bdb4dd2cf1" bob = "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" +adder_abi = testdata / "adder.abi.json" def test_deploy_multisig(capsys: Any): @@ -297,6 +298,655 @@ def test_remove_user(capsys: Any): assert data == "proposeRemoveUser@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8" +def test_change_quorum(capsys: Any): + return_code = main( + [ + "multisig", + "change-quorum", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--quorum", + "10", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "proposeChangeQuorum@0a" + + +def test_transfer_and_execute_with_abi(capsys: Any): + return_code = main( + [ + "multisig", + "transfer-and-execute", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--opt-gas-limit", + "1000000", + "--contract-abi", + str(adder_abi), + "--function", + "add", + "--arguments", + "7", + "--receiver", + "erd1qqqqqqqqqqqqqpgq0rffvv4vk9vesqplv9ws55fxzdfaspqa8cfszy2hms", + "--value", + "1000000000000000000", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert ( + data + == "proposeTransferExecute@0000000000000000050078d29632acb15998003f615d0a51261353d8041d3e13@0de0b6b3a7640000@0100000000000f4240@616464@07" + ) + + +def test_transfer_and_execute_without_abi(capsys: Any): + return_code = main( + [ + "multisig", + "transfer-and-execute", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--opt-gas-limit", + "1000000", + "--function", + "add", + "--arguments", + "0x07", + "--receiver", + "erd1qqqqqqqqqqqqqpgq0rffvv4vk9vesqplv9ws55fxzdfaspqa8cfszy2hms", + "--value", + "1000000000000000000", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert ( + data + == "proposeTransferExecute@0000000000000000050078d29632acb15998003f615d0a51261353d8041d3e13@0de0b6b3a7640000@0100000000000f4240@616464@07" + ) + + +def test_transfer_and_execute_without_execute(capsys: Any): + return_code = main( + [ + "multisig", + "transfer-and-execute", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--receiver", + "erd1qqqqqqqqqqqqqpgq0rffvv4vk9vesqplv9ws55fxzdfaspqa8cfszy2hms", + "--value", + "1000000000000000000", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert ( + data + == "proposeTransferExecute@0000000000000000050078d29632acb15998003f615d0a51261353d8041d3e13@0de0b6b3a7640000@" + ) + + +def test_transfer_and_execute_esdt(capsys: Any): + return_code = main( + [ + "multisig", + "transfer-and-execute-esdt", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--token-transfers", + "ALICE-5627f1", + "10", + "--opt-gas-limit", + "5000000", + "--function", + "distribute", + "--receiver", + "erd1qqqqqqqqqqqqqpgqfxlljcaalgl2qfcnxcsftheju0ts36kvl3ts3qkewe", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert ( + data + == "proposeTransferExecuteEsdt@0000000000000000050049bff963bdfa3ea02713362095df32e3d708eaccfc57@0000000c414c4943452d3536323766310000000000000000000000010a@0100000000004c4b40@3634363937333734373236393632373537343635" + ) + + +def test_async_call(capsys: Any): + return_code = main( + [ + "multisig", + "async-call", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--opt-gas-limit", + "5000000", + "--contract-abi", + str(adder_abi), + "--function", + "add", + "--arguments", + "7", + "--receiver", + "erd1qqqqqqqqqqqqqpgqdvmhpxxmwv2vfz3sfpggzfyl5qznuz5x05vq5y37ql", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert ( + data + == "proposeAsyncCall@000000000000000005006b377098db7314c48a30485081249fa0053e0a867d18@@0100000000004c4b40@616464@07" + ) + + +def test_sc_deploy_from_source(capsys: Any): + return_code = main( + [ + "multisig", + "deploy-from-source", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--contract-to-copy", + "erd1qqqqqqqqqqqqqpgqsuxsgykwm6r3s5apct2g5a2rcpe7kw0ed8ssf6h9f6", + "--contract-abi", + str(adder_abi), + "--arguments", + "0", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + "--value", + "50000000000000000", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert ( + data + == "proposeSCDeployFromSource@b1a2bc2ec50000@00000000000000000500870d0412cede871853a1c2d48a7543c073eb39f969e1@0500@" + ) + + +def test_sc_upgrade_from_source(capsys: Any): + return_code = main( + [ + "multisig", + "upgrade-from-source", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--contract-to-upgrade", + "erd1qqqqqqqqqqqqqpgqsuxsgykwm6r3s5apct2g5a2rcpe7kw0ed8ssf6h9f6", + "--contract-to-copy", + "erd1qqqqqqqqqqqqqpgqsuxsgykwm6r3s5apct2g5a2rcpe7kw0ed8ssf6h9f6", + "--arguments", + "0x00", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "60000000", + "--chain", + "D", + "--value", + "50000000000000000", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 60_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert ( + data + == "proposeSCUpgradeFromSource@00000000000000000500870d0412cede871853a1c2d48a7543c073eb39f969e1@b1a2bc2ec50000@00000000000000000500870d0412cede871853a1c2d48a7543c073eb39f969e1@0500@00" + ) + + +def test_sign_action(capsys: Any): + return_code = main( + [ + "multisig", + "sign-action", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--action", + "7", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "1000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 1_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "sign@07" + + +def test_sign_batch(capsys: Any): + return_code = main( + [ + "multisig", + "sign-batch", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--batch", + "7", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "1000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 1_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "signBatch@07" + + +def test_sign_and_perform(capsys: Any): + return_code = main( + [ + "multisig", + "sign-and-perform", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--action", + "7", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "1000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 1_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "signAndPerform@07" + + +def test_sign_batch_and_perform(capsys: Any): + return_code = main( + [ + "multisig", + "sign-batch-and-perform", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--batch", + "7", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "1000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 1_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "signBatchAndPerform@07" + + +def test_unsign_action(capsys: Any): + return_code = main( + [ + "multisig", + "unsign-action", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--action", + "7", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "1000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 1_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "unsign@07" + + +def test_unsign_batch(capsys: Any): + return_code = main( + [ + "multisig", + "unsign-batch", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--batch", + "7", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "1000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 1_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "unsignBatch@07" + + +def test_unsign_for_outdated_board_members(capsys: Any): + return_code = main( + [ + "multisig", + "unsign-for-outdated-members", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--action", + "7", + "--outdated-members", + "1", + "2", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "1000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 1_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "unsignForOutdatedBoardMembers@07@01@02" + + +def test_perform_action(capsys: Any): + return_code = main( + [ + "multisig", + "perform-action", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--action", + "7", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "1000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 1_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "performAction@07" + + +def test_perform_batch(capsys: Any): + return_code = main( + [ + "multisig", + "perform-batch", + "--contract", + str(contract_address), + "--abi", + str(multisig_abi), + "--batch", + "7", + "--pem", + str(user), + "--nonce", + "0", + "--gas-limit", + "1000000", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == user_address + assert tx["receiver"] == contract_address + assert tx["value"] == "0" + assert tx["gasLimit"] == 1_000_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "performBatch@07" + + def _read_stdout(capsys: Any) -> str: stdout: str = capsys.readouterr().out.strip() return stdout From a649da940d333355bfc1f25c8837473163e1cf2c Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 19 May 2025 11:56:48 +0300 Subject: [PATCH 04/64] convert actions to printable format --- multiversx_sdk_cli/cli_multisig.py | 167 +++++++++++++++++++++++++++-- 1 file changed, 161 insertions(+), 6 deletions(-) diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index 3bdda019..f89a09a3 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -4,10 +4,24 @@ from typing import Any from multiversx_sdk import ( + Action, + ActionFullInfo, + AddBoardMember, + AddProposer, Address, AddressComputer, + CallActionData, + ChangeQuorum, + EsdtTokenPayment, + EsdtTransferExecuteData, MultisigController, ProxyNetworkProvider, + RemoveUser, + SCDeployFromSource, + SCUpgradeFromSource, + SendAsyncCall, + SendTransferExecuteEgld, + SendTransferExecuteEsdt, Token, TokenComputer, TokenTransfer, @@ -2068,7 +2082,6 @@ def is_quorum_reached(args: Any): def get_pending_actions_full_info(args: Any): - # TODO: needs fix validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) @@ -2078,9 +2091,10 @@ def get_pending_actions_full_info(args: Any): contract = Address.new_from_bech32(args.contract) controller = MultisigController(chain_id, proxy, abi) - values = controller.get_pending_actions_full_info(contract) + actions = controller.get_pending_actions_full_info(contract) - print(values) + output = [_convert_action_full_info_to_dict(action) for action in actions] + utils.dump_out_json(output) def get_user_role(args: Any): @@ -2140,7 +2154,6 @@ def get_all_proposers(args: Any): def get_action_data(args: Any): - # TODO: needs fix validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) @@ -2152,8 +2165,8 @@ def get_action_data(args: Any): contract = Address.new_from_bech32(args.contract) action = args.action - value = controller.get_action_data(contract=contract, action_id=action) - print(value) + action_data = controller.get_action_data(contract=contract, action_id=action) + utils.dump_out_json(_convert_action_to_dict(action_data)) def get_action_signers(args: Any): @@ -2254,3 +2267,145 @@ def _send_or_simulate(tx: Transaction, contract_address: Address, args: Any): output_builder = cli_shared.send_or_simulate(tx, args, dump_output=False) output_builder.set_contract_address(contract_address) utils.dump_out_json(output_builder.build(), outfile=args.outfile) + + +def _convert_action_to_dict(action: Action) -> dict[str, Any]: + if isinstance(action, AddBoardMember): + return _convert_add_board_member_to_dict(action) + elif isinstance(action, AddProposer): + return _convert_add_proposer_to_dict(action) + elif isinstance(action, RemoveUser): + return _convert_remove_user_to_dict(action) + elif isinstance(action, ChangeQuorum): + return _convert_change_quorum_to_dict(action) + elif isinstance(action, SendTransferExecuteEgld): + return _convert_send_transfer_execute_egld_to_dict(action) + elif isinstance(action, SendTransferExecuteEsdt): + return _convert_send_transfer_execute_esdt_to_dict(action) + elif isinstance(action, SendAsyncCall): + return _convert_send_async_call_to_dict(action) + elif isinstance(action, SCDeployFromSource): + return _convert_sc_deploy_from_source_to_dict(action) + elif isinstance(action, SCUpgradeFromSource): + return _convert_sc_upgrade_from_source_to_dict(action) + else: + raise Exception(f"Unknown action type: {type(action)}") + + +def _convert_add_board_member_to_dict(action: AddBoardMember) -> dict[str, Any]: + return { + "type": "AddBoardMember", + "discriminant": action.discriminant, + "address": action.address.to_bech32(), + } + + +def _convert_add_proposer_to_dict(action: AddProposer) -> dict[str, Any]: + return { + "type": "AddProposer", + "discriminant": action.discriminant, + "address": action.address.to_bech32(), + } + + +def _convert_remove_user_to_dict(action: RemoveUser) -> dict[str, Any]: + return { + "type": "RemoveUser", + "discriminant": action.discriminant, + "address": action.address.to_bech32(), + } + + +def _convert_change_quorum_to_dict(action: ChangeQuorum) -> dict[str, Any]: + return { + "type": "ChangeQuorum", + "discriminant": action.discriminant, + "quorum": action.quorum, + } + + +def _convert_send_transfer_execute_egld_to_dict(action: SendTransferExecuteEgld) -> dict[str, Any]: + return { + "type": "SendTransferExecuteEgld", + "discriminant": action.discriminant, + "callActionData": _convert_call_action_data_to_dict(action.data), + } + + +def _convert_call_action_data_to_dict(call_action_data: CallActionData) -> dict[str, Any]: + return { + "to": call_action_data.to.to_bech32(), + "egldAmount": call_action_data.egld_amount, + "optGasLimit": call_action_data.opt_gas_limit, + "endpointName": call_action_data.endpoint_name, + "arguments": [arg.hex() for arg in call_action_data.arguments], + } + + +def _convert_send_transfer_execute_esdt_to_dict(action: SendTransferExecuteEsdt) -> dict[str, Any]: + return { + "type": "SendTransferExecuteEsdt", + "discriminant": action.discriminant, + "esdtTransferExecuteData": _convert_esdt_transfer_execute_data_to_dict(action.data), + } + + +def _convert_esdt_transfer_execute_data_to_dict(call_action_data: EsdtTransferExecuteData) -> dict[str, Any]: + return { + "to": call_action_data.to.to_bech32(), + "tokens": _convert_tokens_to_dict(call_action_data.tokens), + "optGasLimit": call_action_data.opt_gas_limit, + "endpointName": call_action_data.endpoint_name, + "arguments": [arg.hex() for arg in call_action_data.arguments], + } + + +def _convert_tokens_to_dict(tokens: list[EsdtTokenPayment]) -> list[dict[str, Any]]: + return [ + { + "tokenIdentifier": token.fields[0].get_payload(), + "tokenNonce": token.fields[1].get_payload(), + "amount": token.fields[2].get_payload(), + } + for token in tokens + ] + + +def _convert_send_async_call_to_dict(action: SendAsyncCall) -> dict[str, Any]: + return { + "type": "SendAsyncCall", + "discriminant": action.discriminant, + "callActionData": _convert_call_action_data_to_dict(action.data), + } + + +def _convert_sc_deploy_from_source_to_dict(action: SCDeployFromSource) -> dict[str, Any]: + return { + "type": "SCDeployFromSource", + "discriminant": action.discriminant, + "amount": action.amount, + "source": action.source.to_bech32(), + "codeMetadata": action.code_metadata.hex(), + "arguments": [arg.hex() for arg in action.arguments], + } + + +def _convert_sc_upgrade_from_source_to_dict(action: SCUpgradeFromSource) -> dict[str, Any]: + return { + "type": "SCDeployFromSource", + "discriminant": action.discriminant, + "scAddress": action.sc_address.to_bech32(), + "amount": action.amount, + "source": action.source.to_bech32(), + "codeMetadata": action.code_metadata.hex(), + "arguments": [arg.hex() for arg in action.arguments], + } + + +def _convert_action_full_info_to_dict(action: ActionFullInfo) -> dict[str, Any]: + return { + "actionId": action.action_id, + "groupId": action.group_id, + "actionData": _convert_action_to_dict(action.action_data), + "signers": [signer.to_bech32() for signer in action.signers], + } From 6ef32e070a5286b11cf3a0a3fc12592fa62c7338 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 19 May 2025 14:33:08 +0300 Subject: [PATCH 05/64] refactoring --- .../base_transactions_controller.py | 64 ++++++++++++- multiversx_sdk_cli/cli_contracts.py | 6 +- multiversx_sdk_cli/cli_multisig.py | 35 ++----- multiversx_sdk_cli/cli_shared.py | 20 ++++ multiversx_sdk_cli/cli_transactions.py | 27 +----- multiversx_sdk_cli/constants.py | 7 ++ multiversx_sdk_cli/contracts.py | 82 +---------------- multiversx_sdk_cli/multisig.py | 91 ++++--------------- 8 files changed, 123 insertions(+), 209 deletions(-) diff --git a/multiversx_sdk_cli/base_transactions_controller.py b/multiversx_sdk_cli/base_transactions_controller.py index 858d81ca..ce3ff4ba 100644 --- a/multiversx_sdk_cli/base_transactions_controller.py +++ b/multiversx_sdk_cli/base_transactions_controller.py @@ -1,14 +1,32 @@ -from typing import Optional, Union +import logging +from typing import Any, Optional, Union -from multiversx_sdk import LedgerAccount, Transaction, TransactionComputer +from multiversx_sdk import Address, LedgerAccount, Transaction, TransactionComputer +from multiversx_sdk.abi import ( + AddressValue, + BigUIntValue, + BoolValue, + BytesValue, + StringValue, +) +from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import ( + ADDRESS_PREFIX, EXTRA_GAS_LIMIT_FOR_GUARDED_TRANSACTIONS, EXTRA_GAS_LIMIT_FOR_RELAYED_TRANSACTIONS, + FALSE_STR_LOWER, + HEX_PREFIX, + MAINCHAIN_ADDRESS_HRP, + STR_PREFIX, + TRUE_STR_LOWER, ) from multiversx_sdk_cli.cosign_transaction import cosign_transaction +from multiversx_sdk_cli.errors import BadUserInput from multiversx_sdk_cli.interfaces import IAccount +logger = logging.getLogger("base_controller") + class BaseTransactionsController: def __init__(self) -> None: @@ -84,3 +102,45 @@ def _sign_guarded_transaction_if_guardian( def _sign_relayed_transaction_if_relayer(self, transaction: Transaction, relayer: Union[IAccount, None]): if relayer and transaction.relayer: transaction.relayer_signature = relayer.sign_transaction(transaction) + + 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(BytesValue(self._hex_to_bytes(arg))) + elif arg.isnumeric(): + args.append(BigUIntValue(int(arg))) + elif arg.startswith(ADDRESS_PREFIX): + args.append(AddressValue.new_from_address(Address.new_from_bech32(arg[len(ADDRESS_PREFIX) :]))) + elif arg.startswith(MAINCHAIN_ADDRESS_HRP): + # this flow will be removed in the future + logger.warning( + "Address argument has no prefix. This flow will be removed in the future. Please provide each address using the `addr:` prefix. (e.g. --arguments addr:erd1...)" + ) + args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) + elif arg.startswith(get_address_hrp()): + args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) + elif arg.lower() == FALSE_STR_LOWER: + args.append(BoolValue(False)) + elif arg.lower() == TRUE_STR_LOWER: + args.append(BoolValue(True)) + elif arg.startswith(STR_PREFIX): + args.append(StringValue(arg[len(STR_PREFIX) :])) + else: + raise BadUserInput( + f"Unknown argument type for argument: `{arg}`. Use `mxpy contract --help` to check all supported arguments" + ) + + return args + + def _hex_to_bytes(self, arg: str): + argument = arg[len(HEX_PREFIX) :] + argument = argument.upper() + argument = self.ensure_even_length(argument) + return bytes.fromhex(argument) + + def ensure_even_length(self, string: str) -> str: + if len(string) % 2 == 1: + return "0" + string + return string diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index cb9df27c..20e3a3b2 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -412,6 +412,10 @@ def call(args: Any): arguments, should_prepare_args = _get_contract_arguments(args) contract_address = Address.new_from_bech32(args.contract) + token_transfers = None + if args.token_transfers: + token_transfers = cli_shared.prepare_token_transfers(args.token_transfers) + tx = contract.prepare_execute_transaction( caller=sender, contract=contract_address, @@ -420,7 +424,7 @@ def call(args: Any): should_prepare_args=should_prepare_args, gas_limit=int(args.gas_limit), value=int(args.value), - transfers=args.token_transfers, + token_transfers=token_transfers, nonce=sender.nonce, version=int(args.version), options=int(args.options), diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index f89a09a3..0fd24726 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -22,9 +22,6 @@ SendAsyncCall, SendTransferExecuteEgld, SendTransferExecuteEsdt, - Token, - TokenComputer, - TokenTransfer, Transaction, TransactionsFactoryConfig, ) @@ -1189,7 +1186,7 @@ def deposit(args: Any): token_transfers = args.token_transfers or None if token_transfers: - token_transfers = _prepare_token_transfers(token_transfers) + token_transfers = cli_shared.prepare_token_transfers(token_transfers) tx = multisig.prepare_deposit_transaction( owner=sender, @@ -1444,7 +1441,7 @@ def transfer_and_execute(args: Any): gas_price=int(args.gas_price), version=int(args.version), options=int(args.options), - should_prepare_args=should_prepare_args, + should_prepare_args_for_factory=should_prepare_args, guardian_and_relayer_data=guardian_and_relayer_data, opt_gas_limit=opt_gas_limit, function=function, @@ -1485,7 +1482,7 @@ def transfer_and_execute_esdt(args: Any): function = args.function if args.function else None contract_abi = Abi.load(Path(args.contract_abi)) if args.contract_abi else None arguments, should_prepare_args = _get_contract_arguments(args) - token_transfers = _prepare_token_transfers(args.token_transfers) + token_transfers = cli_shared.prepare_token_transfers(args.token_transfers) tx = multisig.prepare_transfer_execute_esdt_transaction( owner=sender, @@ -1496,7 +1493,7 @@ def transfer_and_execute_esdt(args: Any): gas_price=int(args.gas_price), version=int(args.version), options=int(args.options), - should_prepare_args=should_prepare_args, + should_prepare_args_for_factory=should_prepare_args, guardian_and_relayer_data=guardian_and_relayer_data, token_transfers=token_transfers, opt_gas_limit=opt_gas_limit, @@ -1534,7 +1531,7 @@ def async_call(args: Any): token_transfers = args.token_transfers or None if token_transfers: - token_transfers = _prepare_token_transfers(args.token_transfers) + token_transfers = cli_shared.prepare_token_transfers(args.token_transfers) tx = multisig.prepare_async_call_transaction( owner=sender, @@ -1545,7 +1542,7 @@ def async_call(args: Any): gas_price=int(args.gas_price), version=int(args.version), options=int(args.options), - should_prepare_args=should_prepare_args, + should_prepare_args_for_factory=should_prepare_args, guardian_and_relayer_data=guardian_and_relayer_data, native_token_amount=int(args.value), token_transfers=token_transfers, @@ -1593,7 +1590,7 @@ def deploy_from_source(args: Any): readable=args.metadata_readable, payable=args.metadata_payable, payable_by_sc=args.metadata_payable_by_sc, - should_prepare_args=should_prepare_args, + should_prepare_args_for_factory=should_prepare_args, guardian_and_relayer_data=guardian_and_relayer_data, native_token_amount=int(args.value), abi=contract_abi, @@ -1640,7 +1637,7 @@ def upgrade_from_source(args: Any): readable=args.metadata_readable, payable=args.metadata_payable, payable_by_sc=args.metadata_payable_by_sc, - should_prepare_args=should_prepare_args, + should_prepare_args_for_factory=should_prepare_args, guardian_and_relayer_data=guardian_and_relayer_data, native_token_amount=int(args.value), abi=contract_abi, @@ -2247,22 +2244,6 @@ def _get_contract_arguments(args: Any) -> tuple[list[Any], bool]: return args.arguments, True -def _prepare_token_transfers(transfers: list[str]) -> list[TokenTransfer]: - token_computer = TokenComputer() - token_transfers: list[TokenTransfer] = [] - - for i in range(0, len(transfers) - 1, 2): - identifier = transfers[i] - amount = int(transfers[i + 1]) - nonce = token_computer.extract_nonce_from_extended_identifier(identifier) - - token = Token(identifier, nonce) - transfer = TokenTransfer(token, amount) - token_transfers.append(transfer) - - return token_transfers - - def _send_or_simulate(tx: Transaction, contract_address: Address, args: Any): output_builder = cli_shared.send_or_simulate(tx, args, dump_output=False) output_builder.set_contract_address(contract_address) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 316b8629..91df6223 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -11,6 +11,9 @@ ApiNetworkProvider, LedgerAccount, ProxyNetworkProvider, + Token, + TokenComputer, + TokenTransfer, Transaction, ) @@ -617,3 +620,20 @@ def prepare_guardian_relayer_data(args: Any) -> GuardianRelayerData: relayer=relayer, relayer_address=relayer_address, ) + + +def prepare_token_transfers(transfers: list[str]) -> list[TokenTransfer]: + """Converts a list of token transfers as received from the CLI to a list of TokenTransfer objects.""" + token_computer = TokenComputer() + token_transfers: list[TokenTransfer] = [] + + for i in range(0, len(transfers) - 1, 2): + identifier = transfers[i] + amount = int(transfers[i + 1]) + nonce = token_computer.extract_nonce_from_extended_identifier(identifier) + + token = Token(identifier, nonce) + transfer = TokenTransfer(token, amount) + token_transfers.append(transfer) + + return token_transfers diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 27785f43..9a5dec4a 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -2,14 +2,7 @@ from pathlib import Path from typing import Any -from multiversx_sdk import ( - Address, - ProxyNetworkProvider, - Token, - TokenComputer, - TokenTransfer, - TransactionComputer, -) +from multiversx_sdk import Address, ProxyNetworkProvider, TransactionComputer from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.args_validation import ( @@ -133,7 +126,7 @@ def create_transaction(args: Any): gas_limit = int(args.gas_limit) if args.gas_limit else 0 transfers = getattr(args, "token_transfers", None) - transfers = prepare_token_transfers(transfers) if transfers else None + transfers = cli_shared.prepare_token_transfers(transfers) if transfers else None chain_id = cli_shared.get_chain_id(args.chain, args.proxy) tx_controller = TransactionsController(chain_id) @@ -155,22 +148,6 @@ def create_transaction(args: Any): cli_shared.send_or_simulate(tx, args) -def prepare_token_transfers(transfers: list[Any]) -> list[TokenTransfer]: - token_computer = TokenComputer() - token_transfers: list[TokenTransfer] = [] - - for i in range(0, len(transfers) - 1, 2): - identifier = transfers[i] - amount = int(transfers[i + 1]) - nonce = token_computer.extract_nonce_from_extended_identifier(identifier) - - token = Token(identifier, nonce) - transfer = TokenTransfer(token, amount) - token_transfers.append(transfer) - - return token_transfers - - def send_transaction(args: Any): validate_proxy_argument(args) diff --git a/multiversx_sdk_cli/constants.py b/multiversx_sdk_cli/constants.py index 9a997542..d851f7e7 100644 --- a/multiversx_sdk_cli/constants.py +++ b/multiversx_sdk_cli/constants.py @@ -19,3 +19,10 @@ TCS_SERVICE_ID = "MultiversXTCSService" EXTRA_GAS_LIMIT_FOR_GUARDED_TRANSACTIONS = 50_000 EXTRA_GAS_LIMIT_FOR_RELAYED_TRANSACTIONS = 50_000 + +HEX_PREFIX = "0x" +FALSE_STR_LOWER = "false" +TRUE_STR_LOWER = "true" +STR_PREFIX = "str:" +ADDRESS_PREFIX = "addr:" +MAINCHAIN_ADDRESS_HRP = "erd" diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index a944c224..179c8b5a 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -9,37 +9,20 @@ SmartContractQuery, SmartContractQueryResponse, SmartContractTransactionsFactory, - Token, - TokenComputer, TokenTransfer, Transaction, TransactionOnNetwork, TransactionsFactoryConfig, ) -from multiversx_sdk.abi import ( - Abi, - AddressValue, - BigUIntValue, - BoolValue, - BytesValue, - StringValue, -) +from multiversx_sdk.abi import Abi from multiversx_sdk_cli import errors from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController -from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount logger = logging.getLogger("contracts") -HEX_PREFIX = "0x" -FALSE_STR_LOWER = "false" -TRUE_STR_LOWER = "true" -STR_PREFIX = "str:" -ADDRESS_PREFIX = "addr:" -MAINCHAIN_ADDRESS_HRP = "erd" - # fmt: off class INetworkProvider(Protocol): @@ -117,14 +100,12 @@ def prepare_execute_transaction( should_prepare_args: bool, gas_limit: int, value: int, - transfers: Union[list[str], None], + token_transfers: Union[list[TokenTransfer], None], nonce: int, version: int, options: int, guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: - token_transfers = self._prepare_token_transfers(transfers) if transfers else [] - args = arguments if arguments else [] if should_prepare_args: args = self._prepare_args_for_factory(args) @@ -136,7 +117,7 @@ def prepare_execute_transaction( gas_limit=gas_limit, arguments=args, native_transfer_amount=value, - token_transfers=token_transfers, + token_transfers=token_transfers or [], ) tx.nonce = nonce tx.version = version @@ -226,60 +207,3 @@ def query_contract( raise errors.QueryContractError("Couldn't query contract: ", e) return response - - def _prepare_token_transfers(self, transfers: list[str]) -> list[TokenTransfer]: - token_computer = TokenComputer() - token_transfers: list[TokenTransfer] = [] - - for i in range(0, len(transfers) - 1, 2): - identifier = transfers[i] - amount = int(transfers[i + 1]) - nonce = token_computer.extract_nonce_from_extended_identifier(identifier) - - token = Token(identifier, nonce) - transfer = TokenTransfer(token, amount) - token_transfers.append(transfer) - - return token_transfers - - def _prepare_args_for_factory(self, arguments: list[str]) -> list[Any]: - args: list[Any] = [] - - for arg in arguments: - if arg.startswith(HEX_PREFIX): - args.append(BytesValue(self._hex_to_bytes(arg))) - elif arg.isnumeric(): - args.append(BigUIntValue(int(arg))) - elif arg.startswith(ADDRESS_PREFIX): - args.append(AddressValue.new_from_address(Address.new_from_bech32(arg[len(ADDRESS_PREFIX) :]))) - elif arg.startswith(MAINCHAIN_ADDRESS_HRP): - # this flow will be removed in the future - logger.warning( - "Address argument has no prefix. This flow will be removed in the future. Please provide each address using the `addr:` prefix. (e.g. --arguments addr:erd1...)" - ) - args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) - elif arg.startswith(get_address_hrp()): - args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) - elif arg.lower() == FALSE_STR_LOWER: - args.append(BoolValue(False)) - elif arg.lower() == TRUE_STR_LOWER: - args.append(BoolValue(True)) - elif arg.startswith(STR_PREFIX): - args.append(StringValue(arg[len(STR_PREFIX) :])) - else: - raise errors.BadUserInput( - f"Unknown argument type for argument: `{arg}`. Use `mxpy contract --help` to check all supported arguments" - ) - - return args - - def _hex_to_bytes(self, arg: str): - argument = arg[len(HEX_PREFIX) :] - argument = argument.upper() - argument = self.ensure_even_length(argument) - return bytes.fromhex(argument) - - def ensure_even_length(self, string: str) -> str: - if len(string) % 2 == 1: - return "0" + string - return string diff --git a/multiversx_sdk_cli/multisig.py b/multiversx_sdk_cli/multisig.py index 200e0860..d59cd7cb 100644 --- a/multiversx_sdk_cli/multisig.py +++ b/multiversx_sdk_cli/multisig.py @@ -9,26 +9,9 @@ Transaction, TransactionsFactoryConfig, ) -from multiversx_sdk.abi import ( - Abi, - AddressValue, - BigUIntValue, - BoolValue, - BytesValue, - StringValue, -) +from multiversx_sdk.abi import Abi from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController -from multiversx_sdk_cli.config import get_address_hrp -from multiversx_sdk_cli.contracts import ( - ADDRESS_PREFIX, - FALSE_STR_LOWER, - HEX_PREFIX, - MAINCHAIN_ADDRESS_HRP, - STR_PREFIX, - TRUE_STR_LOWER, -) -from multiversx_sdk_cli.errors import BadUserInput from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount @@ -350,7 +333,7 @@ def prepare_transfer_execute_transaction( gas_price: int, version: int, options: int, - should_prepare_args: bool, + should_prepare_args_for_factory: bool, guardian_and_relayer_data: GuardianRelayerData, opt_gas_limit: Optional[int] = None, abi: Optional[Abi] = None, @@ -358,8 +341,8 @@ def prepare_transfer_execute_transaction( arguments: Optional[list[Any]] = None, ) -> Transaction: args = arguments if arguments else [] - if should_prepare_args: - args = self._prepare_args(args) + if should_prepare_args_for_factory: + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_propose_transfer_execute( sender=owner.address, @@ -400,7 +383,7 @@ def prepare_transfer_execute_esdt_transaction( gas_price: int, version: int, options: int, - should_prepare_args: bool, + should_prepare_args_for_factory: bool, guardian_and_relayer_data: GuardianRelayerData, token_transfers: list[TokenTransfer], opt_gas_limit: Optional[int] = None, @@ -409,8 +392,8 @@ def prepare_transfer_execute_esdt_transaction( arguments: Optional[list[Any]] = None, ) -> Transaction: args = arguments if arguments else [] - if should_prepare_args: - args = self._prepare_args(args) + if should_prepare_args_for_factory: + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_propose_transfer_esdt_execute( sender=owner.address, @@ -451,7 +434,7 @@ def prepare_async_call_transaction( gas_price: int, version: int, options: int, - should_prepare_args: bool, + should_prepare_args_for_factory: bool, guardian_and_relayer_data: GuardianRelayerData, native_token_amount: int = 0, token_transfers: Optional[list[TokenTransfer]] = None, @@ -461,8 +444,8 @@ def prepare_async_call_transaction( arguments: Optional[list[Any]] = None, ) -> Transaction: args = arguments if arguments else [] - if should_prepare_args: - args = self._prepare_args(args) + if should_prepare_args_for_factory: + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_propose_async_call( sender=owner.address, @@ -508,15 +491,15 @@ def prepare_contract_deploy_from_source_transaction( readable: bool, payable: bool, payable_by_sc: bool, - should_prepare_args: bool, + should_prepare_args_for_factory: bool, guardian_and_relayer_data: GuardianRelayerData, native_token_amount: int = 0, abi: Optional[Abi] = None, arguments: Optional[list[Any]] = None, ) -> Transaction: args = arguments if arguments else [] - if should_prepare_args: - args = self._prepare_args(args) + if should_prepare_args_for_factory: + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_propose_contract_deploy_from_source( sender=owner.address, @@ -564,15 +547,15 @@ def prepare_contract_upgrade_from_source_transaction( readable: bool, payable: bool, payable_by_sc: bool, - should_prepare_args: bool, + should_prepare_args_for_factory: bool, guardian_and_relayer_data: GuardianRelayerData, native_token_amount: int = 0, abi: Optional[Abi] = None, arguments: Optional[list[Any]] = None, ) -> Transaction: args = arguments if arguments else [] - if should_prepare_args: - args = self._prepare_args(args) + if should_prepare_args_for_factory: + args = self._prepare_args_for_factory(args) tx = self._factory.create_transaction_for_propose_contract_upgrade_from_source( sender=owner.address, @@ -931,45 +914,3 @@ def prepare_perform_batch_transaction( ) return tx - - def _prepare_args(self, arguments: list[str]) -> list[Any]: - args: list[Any] = [] - - for arg in arguments: - if arg.startswith(HEX_PREFIX): - args.append(BytesValue(self._hex_to_bytes(arg))) - elif arg.isnumeric(): - args.append(BigUIntValue(int(arg))) - elif arg.startswith(ADDRESS_PREFIX): - args.append(AddressValue.new_from_address(Address.new_from_bech32(arg[len(ADDRESS_PREFIX) :]))) - elif arg.startswith(MAINCHAIN_ADDRESS_HRP): - # this flow will be removed in the future - logger.warning( - "Address argument has no prefix. This flow will be removed in the future. Please provide each address using the `addr:` prefix. (e.g. --arguments addr:erd1...)" - ) - args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) - elif arg.startswith(get_address_hrp()): - args.append(AddressValue.new_from_address(Address.new_from_bech32(arg))) - elif arg.lower() == FALSE_STR_LOWER: - args.append(BoolValue(False)) - elif arg.lower() == TRUE_STR_LOWER: - args.append(BoolValue(True)) - elif arg.startswith(STR_PREFIX): - args.append(StringValue(arg[len(STR_PREFIX) :])) - else: - raise BadUserInput( - f"Unknown argument type for argument: `{arg}`. Use `mxpy contract --help` to check all supported arguments" - ) - - return args - - def _hex_to_bytes(self, arg: str): - argument = arg[len(HEX_PREFIX) :] - argument = argument.upper() - argument = self.ensure_even_length(argument) - return bytes.fromhex(argument) - - def ensure_even_length(self, string: str) -> str: - if len(string) % 2 == 1: - return "0" + string - return string From c1fd895226052499d7de9ac1fcbe39f732fb6fea Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 19 May 2025 15:50:57 +0300 Subject: [PATCH 06/64] fix gasPrice for smart contract interactions --- multiversx_sdk_cli/cli_contracts.py | 3 +++ multiversx_sdk_cli/contracts.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index cb9df27c..7714d374 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -373,6 +373,7 @@ def deploy(args: Any): payable=args.metadata_payable, payable_by_sc=args.metadata_payable_by_sc, gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), value=int(args.value), nonce=sender.nonce, version=int(args.version), @@ -419,6 +420,7 @@ def call(args: Any): arguments=arguments, should_prepare_args=should_prepare_args, gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), value=int(args.value), transfers=args.token_transfers, nonce=sender.nonce, @@ -464,6 +466,7 @@ def upgrade(args: Any): payable=args.metadata_payable, payable_by_sc=args.metadata_payable_by_sc, gas_limit=int(args.gas_limit), + gas_price=int(args.gas_price), value=int(args.value), nonce=sender.nonce, version=int(args.version), diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index a944c224..f4b19f2c 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -70,6 +70,7 @@ def prepare_deploy_transaction( payable: bool, payable_by_sc: bool, gas_limit: int, + gas_price: int, value: int, nonce: int, version: int, @@ -94,6 +95,7 @@ def prepare_deploy_transaction( tx.nonce = nonce tx.version = version tx.options = options + tx.gas_price = gas_price tx.guardian = guardian_and_relayer_data.guardian_address tx.relayer = guardian_and_relayer_data.relayer_address @@ -116,6 +118,7 @@ def prepare_execute_transaction( arguments: Union[list[Any], None], should_prepare_args: bool, gas_limit: int, + gas_price: int, value: int, transfers: Union[list[str], None], nonce: int, @@ -141,6 +144,7 @@ def prepare_execute_transaction( tx.nonce = nonce tx.version = version tx.options = options + tx.gas_price = gas_price tx.guardian = guardian_and_relayer_data.guardian_address tx.relayer = guardian_and_relayer_data.relayer_address @@ -167,6 +171,7 @@ def prepare_upgrade_transaction( payable: bool, payable_by_sc: bool, gas_limit: int, + gas_price: int, value: int, nonce: int, version: int, @@ -192,6 +197,7 @@ def prepare_upgrade_transaction( tx.nonce = nonce tx.version = version tx.options = options + tx.gas_price = gas_price tx.guardian = guardian_and_relayer_data.guardian_address tx.relayer = guardian_and_relayer_data.relayer_address From 998b1151eb997f104c7c98bcf2f7e4ffc419c971 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 22 May 2025 12:06:18 +0300 Subject: [PATCH 07/64] use shared args for wait-result and timeout --- multiversx_sdk_cli/cli_contracts.py | 36 +--- multiversx_sdk_cli/cli_multisig.py | 264 +++---------------------- multiversx_sdk_cli/cli_shared.py | 14 ++ multiversx_sdk_cli/cli_transactions.py | 12 +- 4 files changed, 40 insertions(+), 286 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 861970e4..8568a7b4 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -59,17 +59,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) _add_arguments_arg(sub) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -91,17 +81,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: _add_function_arg(sub) _add_arguments_arg(sub) cli_shared.add_token_transfers_args(sub) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -123,17 +103,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) _add_arguments_arg(sub) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index 0fd24726..59fd07bb 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -82,17 +82,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -113,17 +103,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) cli_shared.add_token_transfers_args(sub) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -144,17 +124,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -181,17 +151,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -217,17 +177,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -253,17 +203,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -289,17 +229,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -325,17 +255,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -363,17 +283,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=True, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -402,17 +312,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=True, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -441,17 +341,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=True, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -476,17 +366,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -514,17 +394,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -546,17 +416,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -578,17 +438,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -610,17 +460,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -642,17 +482,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -674,17 +504,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -706,17 +526,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -744,17 +554,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -776,17 +576,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) @@ -808,17 +598,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_proxy_arg(sub) cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 91df6223..bd590b0a 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -282,6 +282,20 @@ def add_token_transfers_args(sub: Any): ) +def add_wait_result_and_timeout_args(sub: Any): + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result" " - only valid if --wait-result is set", + ) + + def parse_omit_fields_arg(args: Any) -> list[str]: literal = args.omit_fields parsed = ast.literal_eval(literal) diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 9a5dec4a..d086a99f 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -44,17 +44,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: cli_shared.add_guardian_wallet_args(args, sub) cli_shared.add_relayed_v3_wallet_args(args, sub) - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", - ) + cli_shared.add_wait_result_and_timeout_args(sub) sub.set_defaults(func=create_transaction) sub = cli_shared.add_command_subparser( From 30118901102b0bef11d675f4327565ae5246eeb5 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 23 May 2025 11:56:41 +0300 Subject: [PATCH 08/64] integrate governance --- multiversx_sdk_cli/cli.py | 2 + multiversx_sdk_cli/cli_governance.py | 492 ++++++++++++++++++ multiversx_sdk_cli/cli_shared.py | 2 +- multiversx_sdk_cli/governance.py | 256 +++++++++ .../tests/test_cli_governance.py | 212 ++++++++ 5 files changed, 963 insertions(+), 1 deletion(-) create mode 100644 multiversx_sdk_cli/cli_governance.py create mode 100644 multiversx_sdk_cli/governance.py create mode 100644 multiversx_sdk_cli/tests/test_cli_governance.py diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 41d3641f..fabb067c 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -16,6 +16,7 @@ import multiversx_sdk_cli.cli_deps import multiversx_sdk_cli.cli_dns import multiversx_sdk_cli.cli_faucet +import multiversx_sdk_cli.cli_governance import multiversx_sdk_cli.cli_ledger import multiversx_sdk_cli.cli_localnet import multiversx_sdk_cli.cli_multisig @@ -124,6 +125,7 @@ def setup_parser(args: list[str]): commands.append(multiversx_sdk_cli.cli_dns.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_faucet.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_multisig.setup_parser(args, subparsers)) + commands.append(multiversx_sdk_cli.cli_governance.setup_parser(args, subparsers)) parser.epilog = """ ---------------------- diff --git a/multiversx_sdk_cli/cli_governance.py b/multiversx_sdk_cli/cli_governance.py new file mode 100644 index 00000000..4105586c --- /dev/null +++ b/multiversx_sdk_cli/cli_governance.py @@ -0,0 +1,492 @@ +from typing import Any + +from multiversx_sdk import ( + Address, + DelegatedVoteInfo, + GovernanceConfig, + GovernanceController, + ProposalInfo, + ProxyNetworkProvider, + TransactionsFactoryConfig, +) + +from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.args_validation import ( + ensure_wallet_args_are_provided, + validate_broadcast_args, + validate_chain_id_args, + validate_proxy_argument, +) +from multiversx_sdk_cli.cli_output import CLIOutputBuilder +from multiversx_sdk_cli.config import get_config_for_network_providers +from multiversx_sdk_cli.governance import GovernanceWrapper + + +def setup_parser(args: list[str], subparsers: Any) -> Any: + parser = cli_shared.add_group_subparser( + subparsers, "governance", "Propose, vote and interact with the governance contract." + ) + subparsers = parser.add_subparsers() + + sub = cli_shared.add_command_subparser( + subparsers, + "governance", + "propose", + f"Create a new governance proposal.{CLIOutputBuilder.describe()}", + ) + + _add_commit_hash_arg(sub) + sub.add_argument("--start-vote-epoch", required=True, type=int, help="the epoch in which the voting will start") + sub.add_argument("--end-vote-epoch", required=True, type=int, help="the epoch in which the voting will stop") + + 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) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + cli_shared.add_outfile_arg(sub) + cli_shared.add_broadcast_args(sub) + cli_shared.add_wait_result_and_timeout_args(sub) + + sub.set_defaults(func=create_proposal) + + sub = cli_shared.add_command_subparser( + subparsers, + "governance", + "vote", + f"Vote for a governance proposal.{CLIOutputBuilder.describe()}", + ) + + _add_proposal_nonce_arg(sub) + sub.add_argument( + "--vote", + required=True, + type=str, + choices=["yes", "no", "veto", "abstain"], + help="the type of vote", + ) + + 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) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + cli_shared.add_outfile_arg(sub) + cli_shared.add_broadcast_args(sub) + cli_shared.add_wait_result_and_timeout_args(sub) + + sub.set_defaults(func=vote) + + sub = cli_shared.add_command_subparser( + subparsers, + "governance", + "close-proposal", + f"Close a governance proposal.{CLIOutputBuilder.describe()}", + ) + + _add_proposal_nonce_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) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + cli_shared.add_outfile_arg(sub) + cli_shared.add_broadcast_args(sub) + cli_shared.add_wait_result_and_timeout_args(sub) + + sub.set_defaults(func=close_proposal) + + sub = cli_shared.add_command_subparser( + subparsers, + "governance", + "clear-ended-proposals", + f"Clear ended proposals.{CLIOutputBuilder.describe()}", + ) + + sub.add_argument( + "--proposers", + nargs="+", + required=True, + type=str, + help="a list of users who initiated the proposals (e.g. --proposers erd1..., erd1...)", + ) + 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) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + cli_shared.add_outfile_arg(sub) + cli_shared.add_broadcast_args(sub) + cli_shared.add_wait_result_and_timeout_args(sub) + + sub.set_defaults(func=clear_ended_proposals) + + sub = cli_shared.add_command_subparser( + subparsers, + "governance", + "claim-accumulated-fees", + f"Claim the accumulated fees.{CLIOutputBuilder.describe()}", + ) + + 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) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + cli_shared.add_outfile_arg(sub) + cli_shared.add_broadcast_args(sub) + cli_shared.add_wait_result_and_timeout_args(sub) + + sub.set_defaults(func=claim_accumulated_fees) + + sub = cli_shared.add_command_subparser( + subparsers, + "governance", + "change-config", + f"Change the config of the contract.{CLIOutputBuilder.describe()}", + ) + + sub.add_argument("--proposal-fee", required=True, type=int, help="the cost to create a new proposal") + sub.add_argument( + "--lost-proposal-fee", + required=True, + type=int, + help="the amount of native tokens the proposer loses if the proposal fails", + ) + sub.add_argument( + "--min-quorum", required=True, type=int, help="the min quorum to be reached for the proposal to pass" + ) + sub.add_argument("--min-veto-threshold", required=True, type=int, help="the min veto threshold") + sub.add_argument("--min-pass-threshold", required=True, type=int, help="the min pass threshold") + + 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) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + cli_shared.add_outfile_arg(sub) + cli_shared.add_broadcast_args(sub) + cli_shared.add_wait_result_and_timeout_args(sub) + + sub.set_defaults(func=change_config) + + sub = cli_shared.add_command_subparser( + subparsers, + "governance", + "get-voting-power", + f"Get the voting power of an user.{CLIOutputBuilder.describe()}", + ) + + sub.add_argument("--user", required=True, type=str, help="the bech32 address of the user") + cli_shared.add_proxy_arg(sub) + + sub.set_defaults(func=get_voting_power) + + sub = cli_shared.add_command_subparser( + subparsers, + "governance", + "get-config", + f"Get the config of the governance contract.{CLIOutputBuilder.describe()}", + ) + + cli_shared.add_proxy_arg(sub) + sub.set_defaults(func=get_config) + + sub = cli_shared.add_command_subparser( + subparsers, + "governance", + "get-proposal", + f"Get info about a proposal.{CLIOutputBuilder.describe()}", + ) + + _add_proposal_nonce_arg(sub) + cli_shared.add_proxy_arg(sub) + sub.set_defaults(func=get_proposal) + + sub = cli_shared.add_command_subparser( + subparsers, + "governance", + "get-delegated-vote-info", + f"Get info about a delegated vote.{CLIOutputBuilder.describe()}", + ) + + sub.add_argument("--contract", required=True, type=str, help="the bech32 address of the contract") + sub.add_argument("--user", required=True, type=str, help="the bech32 address of the user") + cli_shared.add_proxy_arg(sub) + sub.set_defaults(func=get_delegated_vote_info) + + parser.epilog = cli_shared.build_group_epilog(subparsers) + return subparsers + + +def _add_commit_hash_arg(sub: Any): + sub.add_argument("--commit-hash", required=True, type=str, help="the commit hash of the proposal") + + +def _add_proposal_nonce_arg(sub: Any): + sub.add_argument("--proposal-nonce", required=True, type=int, help="the nonce of the proposal") + + +def create_proposal(args: Any): + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + gas_limit = args.gas_limit if args.gas_limit else 0 + + controller = GovernanceWrapper(TransactionsFactoryConfig(chain_id)) + transaction = controller.create_transaction_for_new_proposal( + sender=sender, + nonce=sender.nonce, + commit_hash=args.commit_hash, + start_vote_epoch=args.start_vote_epoch, + end_vote_epoch=args.end_vote_epoch, + native_token_amount=args.value, + gas_limit=gas_limit, + gas_price=args.gas_price, + version=args.version, + options=args.options, + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + cli_shared.send_or_simulate(transaction, args) + + +def vote(args: Any): + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + gas_limit = args.gas_limit if args.gas_limit else 0 + + controller = GovernanceWrapper(TransactionsFactoryConfig(chain_id)) + transaction = controller.create_transaction_for_voting( + sender=sender, + nonce=sender.nonce, + proposal_nonce=args.proposal_nonce, + vote=args.vote, + gas_limit=gas_limit, + gas_price=args.gas_price, + version=args.version, + options=args.options, + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + cli_shared.send_or_simulate(transaction, args) + + +def close_proposal(args: Any): + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + gas_limit = args.gas_limit if args.gas_limit else 0 + + controller = GovernanceWrapper(TransactionsFactoryConfig(chain_id)) + transaction = controller.create_transaction_for_closing_proposal( + sender=sender, + nonce=sender.nonce, + proposal_nonce=args.proposal_nonce, + gas_limit=gas_limit, + gas_price=args.gas_price, + version=args.version, + options=args.options, + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + cli_shared.send_or_simulate(transaction, args) + + +def clear_ended_proposals(args: Any): + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + gas_limit = args.gas_limit if args.gas_limit else 0 + controller = GovernanceWrapper(TransactionsFactoryConfig(chain_id)) + + proposers = [Address.new_from_bech32(proposer) for proposer in args.proposers] + transaction = controller.create_transaction_for_clearing_ended_proposals( + sender=sender, + nonce=sender.nonce, + proposers=proposers, + gas_limit=gas_limit, + gas_price=args.gas_price, + version=args.version, + options=args.options, + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + cli_shared.send_or_simulate(transaction, args) + + +def claim_accumulated_fees(args: Any): + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + gas_limit = args.gas_limit if args.gas_limit else 0 + + controller = GovernanceWrapper(TransactionsFactoryConfig(chain_id)) + transaction = controller.create_transaction_for_claiming_accumulated_fees( + sender=sender, + nonce=sender.nonce, + gas_limit=gas_limit, + gas_price=args.gas_price, + version=args.version, + options=args.options, + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + cli_shared.send_or_simulate(transaction, args) + + +def change_config(args: Any): + ensure_wallet_args_are_provided(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + sender = cli_shared.prepare_sender(args) + guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( + sender=sender.address.to_bech32(), + args=args, + ) + chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + gas_limit = args.gas_limit if args.gas_limit else 0 + + controller = GovernanceWrapper(TransactionsFactoryConfig(chain_id)) + transaction = controller.create_transaction_for_changing_config( + sender=sender, + nonce=sender.nonce, + proposal_fee=args.proposal_fee, + lost_proposal_fee=args.lost_proposal_fee, + min_quorum=args.min_quorum, + min_veto_threshold=args.min_veto_threshold, + min_pass_threshold=args.min_pass_threshold, + gas_limit=gas_limit, + gas_price=args.gas_price, + version=args.version, + options=args.options, + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + cli_shared.send_or_simulate(transaction, args) + + +def get_voting_power(args: Any): + validate_proxy_argument(args) + + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) + chain_id = proxy.get_network_config().chain_id + controller = GovernanceController(chain_id, proxy) + + user = Address.new_from_bech32(args.user) + + voting_power = controller.get_voting_power(user) + print("Voting power: ", voting_power) + + +def get_config(args: Any): + validate_proxy_argument(args) + + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) + chain_id = proxy.get_network_config().chain_id + controller = GovernanceController(chain_id, proxy) + + contract_config = controller.get_config() + utils.dump_out_json(_config_to_dict(contract_config)) + + +def get_proposal(args: Any): + validate_proxy_argument(args) + + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) + chain_id = proxy.get_network_config().chain_id + controller = GovernanceController(chain_id, proxy) + + info = controller.get_proposal(args.proposal_nonce) + utils.dump_out_json(_proposal_to_dict(info)) + + +def get_delegated_vote_info(args: Any): + validate_proxy_argument(args) + + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) + chain_id = proxy.get_network_config().chain_id + controller = GovernanceController(chain_id, proxy) + + contract = Address.new_from_bech32(args.contract) + user = Address.new_from_bech32(args.user) + info = controller.get_delegated_vote_info(contract, user) + utils.dump_out_json(_delegated_vote_info_to_dict(info)) + + +def _config_to_dict(config: GovernanceConfig) -> dict[str, Any]: + return { + "proposal_fee": config.proposal_fee, + "min_quorum": config.min_quorum, + "min_pass_threshold": config.min_pass_threshold, + "min_veto_threshold": config.min_veto_threshold, + "last_proposal_nonce": config.last_proposal_nonce, + } + + +def _proposal_to_dict(proposal: ProposalInfo) -> dict[str, Any]: + return { + "cost": proposal.cost, + "commit_hash": proposal.commit_hash, + "nonce": proposal.nonce, + "issuer": proposal.issuer.to_bech32(), + "start_vote_epoch": proposal.start_vote_epoch, + "end_vote_epoch": proposal.end_vote_epoch, + "quorum_stake": proposal.quorum_stake, + "num_yes_votes": proposal.num_yes_votes, + "num_no_votes": proposal.num_no_votes, + "num_veto_votes": proposal.num_veto_votes, + "num_abstain_votes": proposal.num_abstain_votes, + "is_closed": proposal.is_closed, + "is_passed": proposal.is_passed, + } + + +def _delegated_vote_info_to_dict(delegated_vote_info: DelegatedVoteInfo) -> dict[str, Any]: + return { + "used_stake": delegated_vote_info.used_stake, + "used_power": delegated_vote_info.used_power, + "total_stake": delegated_vote_info.total_stake, + "total_power": delegated_vote_info.total_power, + } diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index bd590b0a..f45071e5 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -128,7 +128,7 @@ def add_tx_args( ) sub.add_argument("--gas-limit", required=False, type=int, help="⛽ the gas limit") - sub.add_argument("--value", default="0", type=int, help="the value to transfer (default: %(default)s)") + sub.add_argument("--value", default=0, type=int, help="the value to transfer (default: %(default)s)") if with_data: sub.add_argument( diff --git a/multiversx_sdk_cli/governance.py b/multiversx_sdk_cli/governance.py new file mode 100644 index 00000000..8d0e6e2b --- /dev/null +++ b/multiversx_sdk_cli/governance.py @@ -0,0 +1,256 @@ +from multiversx_sdk import ( + Address, + GovernanceTransactionsFactory, + Transaction, + TransactionsFactoryConfig, + VoteType, +) + +from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController +from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData +from multiversx_sdk_cli.interfaces import IAccount + + +class GovernanceWrapper(BaseTransactionsController): + def __init__(self, config: TransactionsFactoryConfig) -> None: + self.factory = GovernanceTransactionsFactory(config) + + def create_transaction_for_new_proposal( + self, + sender: IAccount, + nonce: int, + commit_hash: str, + start_vote_epoch: int, + end_vote_epoch: int, + native_token_amount: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self.factory.create_transaction_for_new_proposal( + sender=sender.address, + commit_hash=commit_hash, + start_vote_epoch=start_vote_epoch, + end_vote_epoch=end_vote_epoch, + native_token_amount=native_token_amount, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=sender, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def create_transaction_for_voting( + self, + sender: IAccount, + nonce: int, + proposal_nonce: int, + vote: str, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + vote_value = self._vote_type_from_string(vote) + + tx = self.factory.create_transaction_for_voting( + sender=sender.address, proposal_nonce=proposal_nonce, vote=vote_value + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=sender, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def create_transaction_for_closing_proposal( + self, + sender: IAccount, + nonce: int, + proposal_nonce: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self.factory.create_transaction_for_closing_proposal(sender=sender.address, proposal_nonce=proposal_nonce) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=sender, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def create_transaction_for_clearing_ended_proposals( + self, + sender: IAccount, + nonce: int, + proposers: list[Address], + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self.factory.create_transaction_for_clearing_ended_proposals(sender=sender.address, proposers=proposers) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=sender, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def create_transaction_for_claiming_accumulated_fees( + self, + sender: IAccount, + nonce: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self.factory.create_transaction_for_claiming_accumulated_fees(sender=sender.address) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=sender, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def create_transaction_for_changing_config( + self, + sender: IAccount, + nonce: int, + proposal_fee: int, + lost_proposal_fee: int, + min_quorum: int, + min_veto_threshold: int, + min_pass_threshold: int, + gas_limit: int, + gas_price: int, + version: int, + options: int, + guardian_and_relayer_data: GuardianRelayerData, + ) -> Transaction: + tx = self.factory.create_transaction_for_changing_config( + sender=sender.address, + proposal_fee=proposal_fee, + lost_proposal_fee=lost_proposal_fee, + min_quorum=min_quorum, + min_veto_threshold=min_veto_threshold, + min_pass_threshold=min_pass_threshold, + ) + tx.nonce = nonce + tx.version = version + tx.options = options + tx.gas_price = gas_price + tx.guardian = guardian_and_relayer_data.guardian_address + tx.relayer = guardian_and_relayer_data.relayer_address + + self.add_extra_gas_limit_if_required(tx) + + if gas_limit: + tx.gas_limit = gas_limit + + self.sign_transaction( + transaction=tx, + sender=sender, + guardian=guardian_and_relayer_data.guardian, + relayer=guardian_and_relayer_data.relayer, + guardian_service_url=guardian_and_relayer_data.guardian_service_url, + guardian_2fa_code=guardian_and_relayer_data.guardian_2fa_code, + ) + + return tx + + def _vote_type_from_string(self, value: str) -> VoteType: + for vote in VoteType: + if vote.value == value: + return vote + raise ValueError(f"Unknown vote type: {value}") diff --git a/multiversx_sdk_cli/tests/test_cli_governance.py b/multiversx_sdk_cli/tests/test_cli_governance.py new file mode 100644 index 00000000..c67922d2 --- /dev/null +++ b/multiversx_sdk_cli/tests/test_cli_governance.py @@ -0,0 +1,212 @@ +import base64 +import json +from pathlib import Path +from typing import Any + +from multiversx_sdk_cli.cli import main + +testdata = Path(__file__).parent / "testdata" +alice = testdata / "alice.pem" +alice_address = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" +governance_contract = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla" +commit_hash = "1db734c0315f9ec422b88f679ccfe3e0197b9d67" + + +def test_new_proposal(capsys: Any): + return_code = main( + [ + "governance", + "propose", + "--commit-hash", + commit_hash, + "--start-vote-epoch", + "10", + "--end-vote-epoch", + "15", + "--value", + "1000000000000000000000", + "--pem", + str(alice), + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == alice_address + assert tx["receiver"] == governance_contract + assert tx["value"] == "1000000000000000000000" + assert tx["gasLimit"] == 50_192_500 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == f"proposal@{commit_hash.encode().hex()}@0a@0f" + + +def test_vote(capsys: Any): + return_code = main( + [ + "governance", + "vote", + "--proposal-nonce", + "1", + "--vote", + "yes", + "--pem", + str(alice), + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == alice_address + assert tx["receiver"] == governance_contract + assert tx["value"] == "0" + assert tx["gasLimit"] == 5_171_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "vote@01@796573" + + +def test_close_proposal(capsys: Any): + return_code = main( + [ + "governance", + "close-proposal", + "--proposal-nonce", + "1", + "--pem", + str(alice), + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == alice_address + assert tx["receiver"] == governance_contract + assert tx["value"] == "0" + assert tx["gasLimit"] == 50_074_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "closeProposal@01" + + +def test_clear_ended_proposals(capsys: Any): + return_code = main( + [ + "governance", + "clear-ended-proposals", + "--proposers", + alice_address, + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--pem", + str(alice), + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == alice_address + assert tx["receiver"] == governance_contract + assert tx["value"] == "0" + assert tx["gasLimit"] == 150_273_500 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert ( + data + == "clearEndedProposals@0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8" + ) + + +def test_claim_accumulated_fees(capsys: Any): + return_code = main( + [ + "governance", + "claim-accumulated-fees", + "--pem", + str(alice), + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == alice_address + assert tx["receiver"] == governance_contract + assert tx["value"] == "0" + assert tx["gasLimit"] == 1_080_000 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert data == "claimAccumulatedFees" + + +def test_change_config(capsys: Any): + return_code = main( + [ + "governance", + "change-config", + "--proposal-fee", + "1000000000000000000000", + "--lost-proposal-fee", + "10000000000000000000", + "--min-quorum", + "5000", + "--min-veto-threshold", + "3000", + "--min-pass-threshold", + "6000", + "--pem", + str(alice), + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert not return_code + tx = get_transaction(capsys) + + assert tx["sender"] == alice_address + assert tx["receiver"] == governance_contract + assert tx["value"] == "0" + assert tx["gasLimit"] == 50_237_500 + assert tx["chainID"] == "D" + data = tx["data"] + data = base64.b64decode(data).decode() + assert ( + data + == "changeConfig@31303030303030303030303030303030303030303030@3130303030303030303030303030303030303030@35303030@33303030@36303030" + ) + + +def _read_stdout(capsys: Any) -> str: + stdout: str = capsys.readouterr().out.strip() + return stdout + + +def get_transaction(capsys: Any) -> dict[str, Any]: + out = _read_stdout(capsys) + output: dict[str, Any] = json.loads(out) + return output["emittedTransaction"] From e71c0b1e7b6c19356deda17b7c6d47bd976489f7 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 23 May 2025 11:57:18 +0300 Subject: [PATCH 09/64] update script and generate CLI.md --- CLI.md | 3459 ++++++++++++++++++++++++++++++++++++++++++++++++++++- CLI.md.sh | 53 + 2 files changed, 3511 insertions(+), 1 deletion(-) diff --git a/CLI.md b/CLI.md index 9c8e9845..c494ad69 100644 --- a/CLI.md +++ b/CLI.md @@ -23,7 +23,7 @@ See: COMMAND GROUPS: - {contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet} + {contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet,multisig,governance} TOP-LEVEL OPTIONS: -h, --help show this help message and exit @@ -46,6 +46,8 @@ data Data manipulation omnitool staking-provider Staking provider omnitool dns Operations related to the Domain Name Service faucet Get xEGLD on Devnet or Testnet +multisig Deploy and interact with the Multisig Smart Contract +governance Propose, vote and interact with the governance contract. ``` ## Group **Contract** @@ -129,6 +131,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. @@ -222,6 +225,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. @@ -325,6 +329,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. @@ -416,6 +421,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation ``` ### Contract.ReproducibleBuild @@ -504,6 +510,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -610,6 +617,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --infile INFILE input file (a previously saved transaction) --outfile OUTFILE where to save the output (the signed transaction) (default: stdout) --send ✓ whether to broadcast the transaction (default: False) @@ -724,6 +732,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -780,6 +789,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -834,6 +844,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -888,6 +899,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -942,6 +954,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -996,6 +1009,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1049,6 +1063,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1103,6 +1118,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1157,6 +1173,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1211,6 +1228,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1265,6 +1283,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1318,6 +1337,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1411,6 +1431,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1483,6 +1504,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1539,6 +1561,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1595,6 +1618,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1651,6 +1675,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1707,6 +1732,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1763,6 +1789,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1817,6 +1844,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1871,6 +1899,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1925,6 +1954,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -1979,6 +2009,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -2033,6 +2064,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -2088,6 +2120,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -2143,6 +2176,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -2199,6 +2233,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -2255,6 +2290,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -2312,6 +2348,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -2369,6 +2406,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: @@ -2510,6 +2548,7 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation ``` ### Wallet.VerifyMessage @@ -3030,8 +3069,3426 @@ options: --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation --chain {D,T} the chain identifier --api API custom api url for the native auth client --wallet-url WALLET_URL custom wallet url to call the faucet from ``` +## Group **Multisig** + + +``` +$ mxpy multisig --help +usage: mxpy multisig COMMAND [-h] ... + +Deploy and interact with the Multisig Smart Contract + +COMMANDS: + {deploy,deposit,discard-action,discard-batch,add-board-member,add-proposer,remove-user,change-quorum,transfer-and-execute,transfer-and-execute-esdt,async-call,deploy-from-source,upgrade-from-source,sign-action,sign-batch,sign-and-perform,sign-batch-and-perform,unsign-action,unsign-batch,unsign-for-outdated-members,perform-action,perform-batch,get-quorum,get-num-board-members,get-num-groups,get-num-proposers,get-action-group,get-last-action-group-id,get-action-last-index,is-signed-by,is-quorum-reached,get-pending-actions,get-user-role,get-board-members,get-proposers,get-action-data,get-action-signers,get-action-signers-count,get-action-valid-signers-count,parse-propose-action} + +OPTIONS: + -h, --help show this help message and exit + +---------------- +COMMANDS summary +---------------- +deploy Deploy a Multisig Smart Contract. +deposit Deposit native tokens (EGLD) or ESDT tokens into a Multisig Smart Contract. +discard-action Discard a proposed action. Signatures must be removed first via `unsign`. +discard-batch Discard all the actions for the specified IDs. +add-board-member Propose adding a new board member. +add-proposer Propose adding a new proposer. +remove-user Propose removing a user from the Multisig Smart Contract. +change-quorum Propose changing the quorum of the Multisig Smart Contract. +transfer-and-execute Propose transferring EGLD and optionally calling a smart contract. +transfer-and-execute-esdt Propose transferring ESDTs and optionally calling a smart contract. +async-call Propose a transaction in which the contract will perform an async call. +deploy-from-source Propose a smart contract deploy from a previously deployed smart contract. +upgrade-from-source Propose a smart contract upgrade from a previously deployed smart contract. +sign-action Sign a proposed action. +sign-batch Sign a batch of actions. +sign-and-perform Sign a proposed action and perform it. Works only if quorum is reached. +sign-batch-and-perform Sign a batch of actions and perform them. Works only if quorum is reached. +unsign-action Unsign a proposed action. +unsign-batch Unsign a batch of actions. +unsign-for-outdated-members Unsign an action for outdated board members. +perform-action Perform an action that has reached quorum. +perform-batch Perform a batch of actions that has reached quorum. +get-quorum Perform a smart contract query to get the quorum. +get-num-board-members Perform a smart contract query to get the number of board members. +get-num-groups Perform a smart contract query to get the number of groups. +get-num-proposers Perform a smart contract query to get the number of proposers. +get-action-group Perform a smart contract query to get the actions in a group. +get-last-action-group-id Perform a smart contract query to get the id of the last action in a group. +get-action-last-index Perform a smart contract query to get the index of the last action. +is-signed-by Perform a smart contract query to check if an action is signed by a user. +is-quorum-reached Perform a smart contract query to check if an action has reached quorum. +get-pending-actions Perform a smart contract query to get the pending actions full info. +get-user-role Perform a smart contract query to get the role of a user. +get-board-members Perform a smart contract query to get all the board members. +get-proposers Perform a smart contract query to get all the proposers. +get-action-data Perform a smart contract query to get the data of an action. +get-action-signers Perform a smart contract query to get the signers of an action. +get-action-signers-count Perform a smart contract query to get the number of signers of an action. +get-action-valid-signers-count Perform a smart contract query to get the number of valid signers of an action. +parse-propose-action Parses the propose action transaction to extract proposal ID. + +``` +### Multisig.Deploy + + +``` +$ mxpy multisig deploy --help +usage: mxpy multisig deploy [-h] ... + +Deploy a Multisig Smart Contract. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --bytecode BYTECODE the file containing the WASM bytecode + --abi ABI the ABI file of the Multisig Smart Contract + --quorum QUORUM the number of signatures required to approve a proposal + --board-members BOARD_MEMBERS [BOARD_MEMBERS ...] + the bech32 addresses of the board members + --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) + --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) + --metadata-payable ‼ mark the contract as payable (default: not payable) + --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is + set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.Deposit + + +``` +$ mxpy multisig deposit --help +usage: mxpy multisig deposit [-h] ... + +Deposit native tokens (EGLD) or ESDT tokens into a Multisig Smart Contract. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --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 + --wait-result signal to wait for the transaction result - only valid if --send is + set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.DiscardAction + + +``` +$ mxpy multisig discard-action --help +usage: mxpy multisig discard-action [-h] ... + +Discard a proposed action. Signatures must be removed first via `unsign`. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.DiscardBatch + + +``` +$ mxpy multisig discard-batch --help +usage: mxpy multisig discard-batch [-h] ... + +Discard all the actions for the specified IDs. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action-ids ACTION_IDS [ACTION_IDS ...] the IDs of the actions to discard + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.AddBoardMember + + +``` +$ mxpy multisig add-board-member --help +usage: mxpy multisig add-board-member [-h] ... + +Propose adding a new board member. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --board-member BOARD_MEMBER the bech32 address of the proposed board member + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.AddProposer + + +``` +$ mxpy multisig add-proposer --help +usage: mxpy multisig add-proposer [-h] ... + +Propose adding a new proposer. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --proposer PROPOSER the bech32 address of the proposed proposer + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.RemoveUser + + +``` +$ mxpy multisig remove-user --help +usage: mxpy multisig remove-user [-h] ... + +Propose removing a user from the Multisig Smart Contract. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --user USER the bech32 address of the proposed user to be removed + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.ChangeQuorum + + +``` +$ mxpy multisig change-quorum --help +usage: mxpy multisig change-quorum [-h] ... + +Propose changing the quorum of the Multisig Smart Contract. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --quorum QUORUM the size of the new quorum (number of signatures required to approve a + proposal) + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.TransferAndExecute + + +``` +$ mxpy multisig transfer-and-execute --help +usage: mxpy multisig transfer-and-execute [-h] ... + +Propose transferring EGLD and optionally calling a smart contract. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --opt-gas-limit OPT_GAS_LIMIT the size of the new quorum (number of signatures required to approve a + proposal) + --contract-abi CONTRACT_ABI the ABI file of the contract to call + --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 addr:erd1[..] + --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. + E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --receiver RECEIVER 🖄 the address of the receiver + --receiver-username RECEIVER_USERNAME 🖄 the username of the receiver + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.TransferAndExecuteEsdt + + +``` +$ mxpy multisig transfer-and-execute-esdt --help +usage: mxpy multisig transfer-and-execute-esdt [-h] ... + +Propose transferring ESDTs and optionally calling a smart contract. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --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 + --opt-gas-limit OPT_GAS_LIMIT the size of the new quorum (number of signatures required to approve a + proposal) + --contract-abi CONTRACT_ABI the ABI file of the contract to call + --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 addr:erd1[..] + --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. + E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --receiver RECEIVER 🖄 the address of the receiver + --receiver-username RECEIVER_USERNAME 🖄 the username of the receiver + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is + set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.AsyncCall + + +``` +$ mxpy multisig async-call --help +usage: mxpy multisig async-call [-h] ... + +Propose a transaction in which the contract will perform an async call. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --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 + --opt-gas-limit OPT_GAS_LIMIT the size of the new quorum (number of signatures required to approve a + proposal) + --contract-abi CONTRACT_ABI the ABI file of the contract to call + --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 addr:erd1[..] + --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. + E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --receiver RECEIVER 🖄 the address of the receiver + --receiver-username RECEIVER_USERNAME 🖄 the username of the receiver + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is + set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.DeployFromSource + + +``` +$ mxpy multisig deploy-from-source --help +usage: mxpy multisig deploy-from-source [-h] ... + +Propose a smart contract deploy from a previously deployed smart contract. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --contract-to-copy CONTRACT_TO_COPY the bech32 address of the contract to copy + --contract-abi CONTRACT_ABI the ABI file of the contract to copy + --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, + ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 + 0xabba str:TOK-a1c2ef true addr:erd1[..] + --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. + E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] + --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) + --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) + --metadata-payable ‼ mark the contract as payable (default: not payable) + --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.UpgradeFromSource + + +``` +$ mxpy multisig upgrade-from-source --help +usage: mxpy multisig upgrade-from-source [-h] ... + +Propose a smart contract upgrade from a previously deployed smart contract. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --contract-to-upgrade CONTRACT_TO_UPGRADE the bech32 address of the contract to upgrade + --contract-to-copy CONTRACT_TO_COPY the bech32 address of the contract to copy + --contract-abi CONTRACT_ABI the ABI file of the contract to copy + --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, + ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 + 0xabba str:TOK-a1c2ef true addr:erd1[..] + --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. + E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] + --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) + --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) + --metadata-payable ‼ mark the contract as payable (default: not payable) + --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.SignAction + + +``` +$ mxpy multisig sign-action --help +usage: mxpy multisig sign-action [-h] ... + +Sign a proposed action. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.SignBatch + + +``` +$ mxpy multisig sign-batch --help +usage: mxpy multisig sign-batch [-h] ... + +Sign a batch of actions. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --batch BATCH the id of the batch to sign + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.SignAndPerform + + +``` +$ mxpy multisig sign-and-perform --help +usage: mxpy multisig sign-and-perform [-h] ... + +Sign a proposed action and perform it. Works only if quorum is reached. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.SignBatchAndPerform + + +``` +$ mxpy multisig sign-batch-and-perform --help +usage: mxpy multisig sign-batch-and-perform [-h] ... + +Sign a batch of actions and perform them. Works only if quorum is reached. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --batch BATCH the id of the batch to sign + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.UnsignAction + + +``` +$ mxpy multisig unsign-action --help +usage: mxpy multisig unsign-action [-h] ... + +Unsign a proposed action. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.UnsignBatch + + +``` +$ mxpy multisig unsign-batch --help +usage: mxpy multisig unsign-batch [-h] ... + +Unsign a batch of actions. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --batch BATCH the id of the batch to sign + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.UnsignForOutdatedMembers + + +``` +$ mxpy multisig unsign-for-outdated-members --help +usage: mxpy multisig unsign-for-outdated-members [-h] ... + +Unsign an action for outdated board members. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --outdated-members OUTDATED_MEMBERS [OUTDATED_MEMBERS ...] + IDs of the outdated board members + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is + set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.PerformAction + + +``` +$ mxpy multisig perform-action --help +usage: mxpy multisig perform-action [-h] ... + +Perform an action that has reached quorum. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.PerformBatch + + +``` +$ mxpy multisig perform-batch --help +usage: mxpy multisig perform-batch [-h] ... + +Perform a batch of actions that has reached quorum. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --batch BATCH the id of the batch to sign + --outfile OUTFILE where to save the output (default: stdout) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + +``` +### Multisig.GetQuorum + + +``` +$ mxpy multisig get-quorum --help +usage: mxpy multisig get-quorum [-h] ... + +Perform a smart contract query to get the quorum. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetNumBoardMembers + + +``` +$ mxpy multisig get-num-board-members --help +usage: mxpy multisig get-num-board-members [-h] ... + +Perform a smart contract query to get the number of board members. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetNumGroups + + +``` +$ mxpy multisig get-num-groups --help +usage: mxpy multisig get-num-groups [-h] ... + +Perform a smart contract query to get the number of groups. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetNumProposers + + +``` +$ mxpy multisig get-num-proposers --help +usage: mxpy multisig get-num-proposers [-h] ... + +Perform a smart contract query to get the number of proposers. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetActionGroup + + +``` +$ mxpy multisig get-action-group --help +usage: mxpy multisig get-action-group [-h] ... + +Perform a smart contract query to get the actions in a group. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --group GROUP the group id + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetLastActionGroupId + + +``` +$ mxpy multisig get-last-action-group-id --help +usage: mxpy multisig get-last-action-group-id [-h] ... + +Perform a smart contract query to get the id of the last action in a group. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetLastActionLastIndex + + +``` +$ mxpy multisig get-action-last-index --help +usage: mxpy multisig get-action-last-index [-h] ... + +Perform a smart contract query to get the index of the last action. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.IsSignedBy + + +``` +$ mxpy multisig is-signed-by --help +usage: mxpy multisig is-signed-by [-h] ... + +Perform a smart contract query to check if an action is signed by a user. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --user USER the bech32 address of the user + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.IsQuorumReached + + +``` +$ mxpy multisig is-quorum-reached --help +usage: mxpy multisig is-quorum-reached [-h] ... + +Perform a smart contract query to check if an action has reached quorum. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetPendingActions + + +``` +$ mxpy multisig get-pending-actions --help +usage: mxpy multisig get-pending-actions [-h] ... + +Perform a smart contract query to get the pending actions full info. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetUserRole + + +``` +$ mxpy multisig get-user-role --help +usage: mxpy multisig get-user-role [-h] ... + +Perform a smart contract query to get the role of a user. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --user USER the bech32 address of the user + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetBoardMemebers + + +``` +$ mxpy multisig get-board-members --help +usage: mxpy multisig get-board-members [-h] ... + +Perform a smart contract query to get all the board members. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetProposers + + +``` +$ mxpy multisig get-proposers --help +usage: mxpy multisig get-proposers [-h] ... + +Perform a smart contract query to get all the proposers. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetActionData + + +``` +$ mxpy multisig get-action-data --help +usage: mxpy multisig get-action-data [-h] ... + +Perform a smart contract query to get the data of an action. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetActionSigners + + +``` +$ mxpy multisig get-action-signers --help +usage: mxpy multisig get-action-signers [-h] ... + +Perform a smart contract query to get the signers of an action. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetActionSignersCount + + +``` +$ mxpy multisig get-action-signers-count --help +usage: mxpy multisig get-action-signers-count [-h] ... + +Perform a smart contract query to get the number of signers of an action. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.GetActionValidSignersCount + + +``` +$ mxpy multisig get-action-valid-signers-count --help +usage: mxpy multisig get-action-valid-signers-count [-h] ... + +Perform a smart contract query to get the number of valid signers of an action. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract + --action ACTION the id of the action + --proxy PROXY 🔗 the URL of the proxy + +``` +### Multisig.ParseProposeAction + + +``` +$ mxpy multisig parse-propose-action --help +usage: mxpy multisig parse-propose-action [-h] ... + +Parses the propose action transaction to extract proposal ID. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash", + "contractAddress": "the address of the contract", + "transactionOnNetwork": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "simulation": { + "execution": { + "...": "..." + }, + "cost": { + "...": "..." + } + } +} + +options: + -h, --help show this help message and exit + --abi ABI the ABI file of the Multisig Smart Contract + --hash HASH the transaction hash of the propose action + --proxy PROXY 🔗 the URL of the proxy + +``` +## Group **Governance** + + +``` +$ mxpy governance --help +usage: mxpy governance COMMAND [-h] ... + +Propose, vote and interact with the governance contract. + +COMMANDS: + {propose,vote,close-proposal,clear-ended-proposals,claim-accumulated-fees,change-config,get-voting-power,get-config,get-proposal,get-delegated-vote-info} + +OPTIONS: + -h, --help show this help message and exit + +---------------- +COMMANDS summary +---------------- +propose Create a new governance proposal. +vote Vote for a governance proposal. +close-proposal Close a governance proposal. +clear-ended-proposals Clear ended proposals. +claim-accumulated-fees Claim the accumulated fees. +change-config Change the config of the contract. +get-voting-power Get the voting power of an user. +get-config Get the config of the governance contract. +get-proposal Get info about a proposal. +get-delegated-vote-info Get info about a delegated vote. + +``` +### Governance.Propose + + +``` +$ mxpy governance propose --help +usage: mxpy governance propose [-h] ... + +Create a new governance proposal. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" +} + +options: + -h, --help show this help message and exit + --commit-hash COMMIT_HASH the commit hash of the proposal + --start-vote-epoch START_VOTE_EPOCH the epoch in which the voting will start + --end-vote-epoch END_VOTE_EPOCH the epoch in which the voting will stop + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --outfile OUTFILE where to save the output (default: stdout) + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + +``` +### Governance.Vote + + +``` +$ mxpy governance vote --help +usage: mxpy governance vote [-h] ... + +Vote for a governance proposal. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" +} + +options: + -h, --help show this help message and exit + --proposal-nonce PROPOSAL_NONCE the nonce of the proposal + --vote {yes,no,veto,abstain} the type of vote + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --outfile OUTFILE where to save the output (default: stdout) + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + +``` +### Governance.CloseProposal + + +``` +$ mxpy governance close-proposal --help +usage: mxpy governance close-proposal [-h] ... + +Close a governance proposal. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" +} + +options: + -h, --help show this help message and exit + --proposal-nonce PROPOSAL_NONCE the nonce of the proposal + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --outfile OUTFILE where to save the output (default: stdout) + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + +``` +### Governance.ClearEndedProposals + + +``` +$ mxpy governance clear-ended-proposals --help +usage: mxpy governance clear-ended-proposals [-h] ... + +Clear ended proposals. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" +} + +options: + -h, --help show this help message and exit + --proposers PROPOSERS [PROPOSERS ...] a list of users who initiated the proposals (e.g. --proposers erd1..., + erd1...) + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --outfile OUTFILE where to save the output (default: stdout) + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + +``` +### Governance.ClaimAccumulatedFees + + +``` +$ mxpy governance claim-accumulated-fees --help +usage: mxpy governance claim-accumulated-fees [-h] ... + +Claim the accumulated fees. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" +} + +options: + -h, --help show this help message and exit + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --outfile OUTFILE where to save the output (default: stdout) + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + +``` +### Governance.ChangeConfig + + +``` +$ mxpy governance change-config --help +usage: mxpy governance change-config [-h] ... + +Change the config of the contract. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" +} + +options: + -h, --help show this help message and exit + --proposal-fee PROPOSAL_FEE the cost to create a new proposal + --lost-proposal-fee LOST_PROPOSAL_FEE the amount of native tokens the proposer loses if the proposal fails + --min-quorum MIN_QUORUM the min quorum to be reached for the proposal to pass + --min-veto-threshold MIN_VETO_THRESHOLD the min veto threshold + --min-pass-threshold MIN_PASS_THRESHOLD the min pass threshold + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: + False). This argument is OBSOLETE. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service + --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian + --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided + --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --guardian-ledger 🔐 bool flag for signing transaction using ledger + --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not + provided, you'll be prompted to enter the password. + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type + mnemonic or Ledger devices (default: 0) + --outfile OUTFILE where to save the output (default: stdout) + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set + +``` +### Governance.GetVotingPower + + +``` +$ mxpy governance get-voting-power --help +usage: mxpy governance get-voting-power [-h] ... + +Get the voting power of an user. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" +} + +options: + -h, --help show this help message and exit + --user USER the bech32 address of the user + --proxy PROXY 🔗 the URL of the proxy + +``` +### Governance.GetConfig + + +``` +$ mxpy governance get-config --help +usage: mxpy governance get-config [-h] ... + +Get the config of the governance contract. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" +} + +options: + -h, --help show this help message and exit + --proxy PROXY 🔗 the URL of the proxy + +``` +### Governance.GetDelegatedVoteInfo + + +``` +$ mxpy governance get-delegated-vote-info --help +usage: mxpy governance get-delegated-vote-info [-h] ... + +Get info about a delegated vote. + +Output example: +=============== +{ + "emittedTransaction": { + "nonce": 42, + "sender": "alice", + "receiver": "bob", + "...": "..." + }, + "emittedTransactionData": "the transaction data, not encoded", + "emittedTransactionHash": "the transaction hash" +} + +options: + -h, --help show this help message and exit + --contract CONTRACT the bech32 address of the contract + --user USER the bech32 address of the user + --proxy PROXY 🔗 the URL of the proxy + +``` diff --git a/CLI.md.sh b/CLI.md.sh index 46ebfb16..24df2640 100755 --- a/CLI.md.sh +++ b/CLI.md.sh @@ -130,6 +130,59 @@ generate() { group "Faucet" "faucet" command "Faucet.Request" "faucet request" + + group "Multisig" "multisig" + command "Multisig.Deploy" "multisig deploy" + command "Multisig.Deposit" "multisig deposit" + command "Multisig.DiscardAction" "multisig discard-action" + command "Multisig.DiscardBatch" "multisig discard-batch" + command "Multisig.AddBoardMember" "multisig add-board-member" + command "Multisig.AddProposer" "multisig add-proposer" + command "Multisig.RemoveUser" "multisig remove-user" + command "Multisig.ChangeQuorum" "multisig change-quorum" + command "Multisig.TransferAndExecute" "multisig transfer-and-execute" + command "Multisig.TransferAndExecuteEsdt" "multisig transfer-and-execute-esdt" + command "Multisig.AsyncCall" "multisig async-call" + command "Multisig.DeployFromSource" "multisig deploy-from-source" + command "Multisig.UpgradeFromSource" "multisig upgrade-from-source" + command "Multisig.SignAction" "multisig sign-action" + command "Multisig.SignBatch" "multisig sign-batch" + command "Multisig.SignAndPerform" "multisig sign-and-perform" + command "Multisig.SignBatchAndPerform" "multisig sign-batch-and-perform" + command "Multisig.UnsignAction" "multisig unsign-action" + command "Multisig.UnsignBatch" "multisig unsign-batch" + command "Multisig.UnsignForOutdatedMembers" "multisig unsign-for-outdated-members" + command "Multisig.PerformAction" "multisig perform-action" + command "Multisig.PerformBatch" "multisig perform-batch" + command "Multisig.GetQuorum" "multisig get-quorum" + command "Multisig.GetNumBoardMembers" "multisig get-num-board-members" + command "Multisig.GetNumGroups" "multisig get-num-groups" + command "Multisig.GetNumProposers" "multisig get-num-proposers" + command "Multisig.GetActionGroup" "multisig get-action-group" + command "Multisig.GetLastActionGroupId" "multisig get-last-action-group-id" + command "Multisig.GetLastActionLastIndex" "multisig get-action-last-index" + command "Multisig.IsSignedBy" "multisig is-signed-by" + command "Multisig.IsQuorumReached" "multisig is-quorum-reached" + command "Multisig.GetPendingActions" "multisig get-pending-actions" + command "Multisig.GetUserRole" "multisig get-user-role" + command "Multisig.GetBoardMemebers" "multisig get-board-members" + command "Multisig.GetProposers" "multisig get-proposers" + command "Multisig.GetActionData" "multisig get-action-data" + command "Multisig.GetActionSigners" "multisig get-action-signers" + command "Multisig.GetActionSignersCount" "multisig get-action-signers-count" + command "Multisig.GetActionValidSignersCount" "multisig get-action-valid-signers-count" + command "Multisig.ParseProposeAction" "multisig parse-propose-action" + + group "Governance" "governance" + command "Governance.Propose" "governance propose" + command "Governance.Vote" "governance vote" + command "Governance.CloseProposal" "governance close-proposal" + command "Governance.ClearEndedProposals" "governance clear-ended-proposals" + command "Governance.ClaimAccumulatedFees" "governance claim-accumulated-fees" + command "Governance.ChangeConfig" "governance change-config" + command "Governance.GetVotingPower" "governance get-voting-power" + command "Governance.GetConfig" "governance get-config" + command "Governance.GetDelegatedVoteInfo" "governance get-delegated-vote-info" } generate From fde5605029fcb46fa97111933cf0bf8b4eb68da0 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 26 May 2025 16:36:36 +0300 Subject: [PATCH 10/64] config cleanup --- multiversx_sdk_cli/cli.py | 5 +- multiversx_sdk_cli/cli_config.py | 22 +++++--- multiversx_sdk_cli/config.py | 93 ++++++++------------------------ multiversx_sdk_cli/errors.py | 7 +++ 4 files changed, 45 insertions(+), 82 deletions(-) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 00519941..d7d024d6 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -43,10 +43,9 @@ 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) + 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( diff --git a/multiversx_sdk_cli/cli_config.py b/multiversx_sdk_cli/cli_config.py index 3d536af0..98981feb 100644 --- a/multiversx_sdk_cli/cli_config.py +++ b/multiversx_sdk_cli/cli_config.py @@ -10,10 +10,10 @@ def setup_parser(subparsers: Any) -> Any: - parser = cli_shared.add_group_subparser(subparsers, "config", "Configure multiversx-sdk (default values etc.)") + parser = cli_shared.add_group_subparser(subparsers, "config", "Configure MultiversX CLI (default values etc.)") subparsers = parser.add_subparsers() - sub = cli_shared.add_command_subparser(subparsers, "config", "dump", "Dumps configuration.") + sub = cli_shared.add_command_subparser(subparsers, "config", "dump", "Dumps the active configuration.") sub.add_argument( "--defaults", required=False, @@ -22,20 +22,28 @@ def setup_parser(subparsers: Any) -> Any: ) sub.set_defaults(func=dump) - sub = cli_shared.add_command_subparser(subparsers, "config", "get", "Gets a configuration value.") + sub = cli_shared.add_command_subparser( + subparsers, "config", "get", "Gets a configuration value from the active configuration." + ) _add_name_arg(sub) sub.set_defaults(func=get_value) - sub = cli_shared.add_command_subparser(subparsers, "config", "set", "Sets a configuration value.") + sub = cli_shared.add_command_subparser( + subparsers, "config", "set", "Sets a configuration value for the active configuration." + ) _add_name_arg(sub) sub.add_argument("value", help="the new value") sub.set_defaults(func=set_value) - sub = cli_shared.add_command_subparser(subparsers, "config", "delete", "Deletes a configuration value.") + sub = cli_shared.add_command_subparser( + subparsers, "config", "delete", "Deletes a configuration value from the active configuration." + ) _add_name_arg(sub) sub.set_defaults(func=delete_value) - sub = cli_shared.add_command_subparser(subparsers, "config", "new", "Creates a new configuration.") + sub = cli_shared.add_command_subparser( + subparsers, "config", "new", "Creates a new configuration and sets it as the active configuration." + ) _add_name_arg(sub) sub.add_argument( "--template", @@ -44,7 +52,7 @@ def setup_parser(subparsers: Any) -> Any: ) sub.set_defaults(func=new_config) - sub = cli_shared.add_command_subparser(subparsers, "config", "switch", "Switch to a different config") + sub = cli_shared.add_command_subparser(subparsers, "config", "switch", "Switch to a different config.") _add_name_arg(sub) sub.set_defaults(func=switch_config) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index ee83f3c5..e996f98d 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -11,23 +11,6 @@ GLOBAL_CONFIG_PATH = SDK_PATH / "mxpy.json" -class MetaChainSystemSCsCost: - STAKE = 5000000 - UNSTAKE = 5000000 - UNBOND = 5000000 - CLAIM = 5000000 - GET = 5000000 - CHANGE_REWARD_ADDRESS = 5000000 - CHANGE_VALIDATOR_KEYS = 5000000 - UNJAIL = 5000000 - DELEGATION_MANAGER_OPS = 50000000 - DELEGATION_OPS = 1000000 - UNSTAKE_TOKENS = 5000000 - UNBOND_TOKENS = 5000000 - CLEAN_REGISTERED_DATA = 5000000 - RESTAKE_UNSTAKED_NODES = 5000000 - - def get_dependency_resolution(key: str) -> str: try: return get_value(f"dependencies.{key}.resolution") @@ -145,28 +128,41 @@ 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": "stable", "dependencies.golang.resolution": "SDK", "dependencies.golang.tag": "go1.20.7", "dependencies.golang.urlTemplate.linux": "https://golang.org/dl/{TAG}.linux-amd64.tar.gz", "dependencies.golang.urlTemplate.osx": "https://golang.org/dl/{TAG}.darwin-amd64.tar.gz", "dependencies.golang.urlTemplate.windows": "https://golang.org/dl/{TAG}.windows-amd64.zip", - "dependencies.twiggy.tag": "", - "dependencies.sc-meta.tag": "", "dependencies.testwallets.tag": "v1.0.0", "dependencies.testwallets.urlTemplate.linux": "https://github.com/multiversx/mx-sdk-testwallets/archive/{TAG}.tar.gz", "dependencies.testwallets.urlTemplate.osx": "https://github.com/multiversx/mx-sdk-testwallets/archive/{TAG}.tar.gz", "dependencies.testwallets.urlTemplate.windows": "https://github.com/multiversx/mx-sdk-testwallets/archive/{TAG}.tar.gz", - "dependencies.wasm-opt.tag": "0.112.0", "github_api_token": "", "default_address_hrp": "erd", + "proxy_url": "", + "explorer_url": "", + "ask_confirmation": "true", } +def get_proxy_url() -> str: + return get_value("proxy_url") + + +def get_explorer_url() -> str: + return get_value("explorer_url") + + +def get_confirmation_setting() -> bool: + confirmation_value = get_value("ask_confirmation") + if confirmation_value.lower() in ["true", "yes", "1"]: + return True + elif confirmation_value.lower() in ["false", "no", "0"]: + return False + else: + raise errors.InvalidConfirmationSettingError(confirmation_value) + + def get_deprecated_entries_in_config_file(): default_config_keys = set(get_defaults().keys()) current_config_keys = set(get_active().keys()) @@ -192,53 +188,6 @@ def write_file(data: dict[str, Any]): utils.write_json_file(str(config_path), data) -def add_config_args(argv: list[str]) -> list[str]: - try: - command, subcommand, *_ = argv - except ValueError: - return argv - - config = read_file() - - try: - config_args = config[command][subcommand] - except KeyError: - return argv - - final_args = determine_final_args(argv, config_args) - print(f"Found extra arguments in mxpy.json. Final arguments: {final_args}") - return final_args - - -def determine_final_args(argv: list[str], config_args: dict[str, Any]) -> list[str]: - extra_args: list[str] = [] - for key, value in config_args.items(): - key_arg = f"--{key}" - # arguments from the command line override the config - if key_arg in argv: - continue - if any(arg.startswith(f"{key_arg}=") for arg in argv): - continue - extra_args.append(key_arg) - if value is True: - continue - if isinstance(value, list): - for item in value: # type: ignore - extra_args.append(str(item)) # type: ignore - else: - extra_args.append(str(value)) - - # the verbose flag is an exception since it has to go before the command and subcommand - # eg. mxpy --verbose contract deploy - verbose_flag = "--verbose" - pre_args = [] - if verbose_flag in extra_args: - extra_args.remove(verbose_flag) - pre_args = [verbose_flag] - - return pre_args + argv + extra_args - - def get_dependency_directory(key: str, tag: str) -> Path: parent_directory = get_dependency_parent_directory(key) return parent_directory / tag diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 577d6207..eb8025d7 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -149,3 +149,10 @@ def __init__(self, message: str): class InvalidArgumentsError(KnownError): def __init__(self, message: str): super().__init__(message) + + +class InvalidConfirmationSettingError(KnownError): + def __init__(self, value: str): + super().__init__( + f"Invalid confirmation setting: {value}. Valid values are: ['true', 'false', 'yes', 'no', '1', '0']." + ) From afba2a8926d962720e4fe51c2ab01d3dc0aa95be Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 27 May 2025 13:48:20 +0300 Subject: [PATCH 11/64] allow users to set proxy url in config --- multiversx_sdk_cli/cli.py | 9 ++++ multiversx_sdk_cli/cli_contracts.py | 16 ++++++- multiversx_sdk_cli/cli_delegation.py | 59 +++++++++++++++++++++++++- multiversx_sdk_cli/cli_dns.py | 14 +++++- multiversx_sdk_cli/cli_shared.py | 35 ++++++++++++++- multiversx_sdk_cli/cli_transactions.py | 16 ++++++- multiversx_sdk_cli/cli_validators.py | 34 +++++++++++++++ multiversx_sdk_cli/config.py | 20 ++++++++- multiversx_sdk_cli/utils.py | 44 ++++++++++++------- 9 files changed, 224 insertions(+), 23 deletions(-) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index d7d024d6..6876d719 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -45,6 +45,8 @@ def _do_main(cli_args: list[str]): utils.ensure_folder(config.SDK_PATH) parser = setup_parser(cli_args) argcomplete.autocomplete(parser) + + _handle_verbose_argument(cli_args) args = parser.parse_args(cli_args) if args.verbose: @@ -146,6 +148,13 @@ def verify_deprecated_entries_in_config_file(): ux.show_warning(message.rstrip("\n")) +def _handle_verbose_argument(args: list[str]): + verbose_arg = "--verbose" + if verbose_arg in args: + args.remove(verbose_arg) + args.insert(0, verbose_arg) + + if __name__ == "__main__": ret = main(sys.argv[1:]) sys.exit(ret) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index a5a54506..825622cc 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -25,7 +25,7 @@ validate_transaction_args, ) from multiversx_sdk_cli.cli_output import CLIOutputBuilder -from multiversx_sdk_cli.config import get_config_for_network_providers +from multiversx_sdk_cli.config import MxpyConfig, get_config_for_network_providers from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS from multiversx_sdk_cli.contract_verification import trigger_contract_verification from multiversx_sdk_cli.contracts import SmartContract @@ -369,13 +369,16 @@ def build(args: Any): The primary tool for building smart contracts is `sc-meta`. To install `sc-meta` check out the documentation: https://docs.multiversx.com/sdk-and-tools/troubleshooting/rust-setup. -After installing, use the `sc-meta all build` command. To lear more about `sc-meta`, check out this page: https://docs.multiversx.com/developers/meta/sc-meta-cli/#calling-build.""" +After installing, use the `sc-meta all build` command. To learn more about `sc-meta`, check out this page: https://docs.multiversx.com/developers/meta/sc-meta-cli/#calling-build.""" show_warning(message) def deploy(args: Any): logger.debug("deploy") + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -425,6 +428,9 @@ def deploy(args: Any): def call(args: Any): logger.debug("call") + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -467,6 +473,9 @@ def call(args: Any): def upgrade(args: Any): logger.debug("upgrade") + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -512,6 +521,9 @@ def upgrade(args: Any): def query(args: Any): logger.debug("query") + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_proxy_argument(args) # we don't need chainID to query a contract; we use the provided proxy diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index ab7e27bb..973ca898 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -19,7 +19,7 @@ validate_proxy_argument, validate_receiver_args, ) -from multiversx_sdk_cli.config import get_config_for_network_providers +from multiversx_sdk_cli.config import MxpyConfig, get_config_for_network_providers from multiversx_sdk_cli.delegation import DelegationOperations @@ -402,6 +402,9 @@ def _get_delegation_controller(args: Any): def do_create_delegation_contract(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -430,6 +433,9 @@ def do_create_delegation_contract(args: Any): def get_contract_address_by_deploy_tx_hash(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_proxy_argument(args) config = get_config_for_network_providers() @@ -447,6 +453,9 @@ def get_contract_address_by_deploy_tx_hash(args: Any): def add_new_nodes(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -495,6 +504,9 @@ def _get_public_keys_and_signed_messages(args: Any) -> tuple[list[ValidatorPubli def remove_nodes(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) @@ -544,6 +556,9 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def stake_nodes(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -582,6 +597,9 @@ def _check_if_either_bls_keys_or_validators_file_are_provided(args: Any): def unbond_nodes(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -612,6 +630,9 @@ def unbond_nodes(args: Any): def unstake_nodes(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -642,6 +663,9 @@ def unstake_nodes(args: Any): def unjail_nodes(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -672,6 +696,9 @@ def unjail_nodes(args: Any): def delegate(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -700,6 +727,9 @@ def delegate(args: Any): def claim_rewards(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -726,6 +756,9 @@ def claim_rewards(args: Any): def redelegate_rewards(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -752,6 +785,9 @@ def redelegate_rewards(args: Any): def undelegate(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -780,6 +816,9 @@ def undelegate(args: Any): def withdraw(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -807,6 +846,9 @@ def withdraw(args: Any): def change_service_fee(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -835,6 +877,9 @@ def change_service_fee(args: Any): def modify_delegation_cap(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -863,6 +908,9 @@ def modify_delegation_cap(args: Any): def automatic_activation(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -892,6 +940,9 @@ def automatic_activation(args: Any): def redelegate_cap(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -921,6 +972,9 @@ def redelegate_cap(args: Any): def set_metadata(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -951,6 +1005,9 @@ def set_metadata(args: Any): def make_new_contract_from_validator_data(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_arguments(args) sender = cli_shared.prepare_sender(args) diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index 2568a06d..f028148f 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -5,7 +5,7 @@ from rich.table import Table from multiversx_sdk_cli import cli_shared -from multiversx_sdk_cli.config import get_config_for_network_providers +from multiversx_sdk_cli.config import MxpyConfig, get_config_for_network_providers from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX from multiversx_sdk_cli.dns import ( compute_dns_address_for_shard_id, @@ -136,6 +136,9 @@ def _ensure_proxy_is_provided(args: Any): def dns_resolve(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + _ensure_proxy_is_provided(args) config = get_config_for_network_providers() @@ -145,6 +148,9 @@ def dns_resolve(args: Any): def dns_validate_name(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + _ensure_proxy_is_provided(args) config = get_config_for_network_providers() @@ -168,6 +174,9 @@ def get_dns_address_for_name_hex(args: Any): def get_registration_cost(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + _ensure_proxy_is_provided(args) config = get_config_for_network_providers() @@ -175,6 +184,9 @@ def get_registration_cost(args: Any): def get_version(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + _ensure_proxy_is_provided(args) config = get_config_for_network_providers() diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 316b8629..eb7ac39f 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -1,5 +1,7 @@ import argparse import ast +import base64 +import logging import sys from argparse import FileType from pathlib import Path @@ -36,7 +38,10 @@ from multiversx_sdk_cli.simulation import Simulator from multiversx_sdk_cli.transactions import send_and_wait_for_result from multiversx_sdk_cli.utils import log_explorer_transaction -from multiversx_sdk_cli.ux import show_warning +from multiversx_sdk_cli.ux import confirm_continuation, show_warning + +logger = logging.getLogger("cli_shared") + trusted_cosigner_service_url_by_chain_id = { "1": "https://tools.multiversx.com/guardian", @@ -557,11 +562,17 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL output_builder.set_emitted_transaction(tx) outfile = args.outfile if hasattr(args, "outfile") else None + cli_config = config.MxpyConfig.from_active_config() + hash = "" try: if send_wait_result: + _confirm_continuation_if_required(cli_config, tx) + transaction_on_network = send_and_wait_for_result(tx, proxy, args.timeout) output_builder.set_awaited_transaction(transaction_on_network) elif send_only: + _confirm_continuation_if_required(cli_config, tx) + hash = proxy.send_transaction(tx) output_builder.set_emitted_transaction_hash(hash.hex()) elif simulate: @@ -573,7 +584,7 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL if dump_output: utils.dump_out_json(output_transaction, outfile=outfile) - if send_only: + if send_only and hash: log_explorer_transaction( chain=output_transaction["emittedTransaction"]["chainID"], transaction_hash=output_transaction["emittedTransactionHash"], @@ -582,6 +593,18 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL return output_builder +def _confirm_continuation_if_required(config: config.MxpyConfig, tx: Transaction) -> None: + if config.ask_confirmation: + transaction = tx.to_dictionary() + + # decode the data field from base64 if it exists + data = base64.b64decode(transaction.get("data", "")).decode() + transaction["data"] = data if data else "" + + utils.dump_out_json(transaction) + confirm_continuation("You are about to send the above transaction. Do you want to continue?") + + def prepare_sender(args: Any): """Returns the sender's account. If no account was provided, will raise an exception.""" @@ -617,3 +640,11 @@ def prepare_guardian_relayer_data(args: Any) -> GuardianRelayerData: relayer=relayer, relayer_address=relayer_address, ) + + +def set_proxy_from_config_if_not_provided(args: Any, config: config.MxpyConfig) -> None: + """This function modifies the `args` object by setting the proxy from the config if not already set.""" + if not args.proxy: + if config.proxy_url: + logger.info(f"Using proxy URL from config: {config.proxy_url}") + args.proxy = config.proxy_url diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 27785f43..e3b96de0 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -23,7 +23,7 @@ ) from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.cli_output import CLIOutputBuilder -from multiversx_sdk_cli.config import get_config_for_network_providers +from multiversx_sdk_cli.config import MxpyConfig, get_config_for_network_providers from multiversx_sdk_cli.errors import BadUsage, IncorrectWalletError, NoWalletProvided from multiversx_sdk_cli.transactions import ( TransactionsController, @@ -114,6 +114,9 @@ def _add_common_arguments(args: list[str], sub: Any): def create_transaction(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_nonce_args(args) validate_receiver_args(args) ensure_wallet_args_are_provided(args) @@ -172,6 +175,9 @@ def prepare_token_transfers(transfers: list[Any]) -> list[TokenTransfer]: def send_transaction(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_proxy_argument(args) tx = load_transaction_from_file(args.infile) @@ -181,6 +187,8 @@ def send_transaction(args: Any): proxy = ProxyNetworkProvider(url=args.proxy, config=config) try: + cli_shared._confirm_continuation_if_required(cli_config, tx) + tx_hash = proxy.send_transaction(tx) output.set_emitted_transaction_hash(tx_hash.hex()) finally: @@ -189,6 +197,9 @@ def send_transaction(args: Any): def sign_transaction(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_broadcast_args(args) tx = load_transaction_from_file(args.infile) @@ -228,6 +239,9 @@ def sign_transaction(args: Any): def relay_transaction(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + ensure_relayer_wallet_args_are_provided(args) validate_broadcast_args(args) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 7d06feb9..5087e0a9 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -11,6 +11,7 @@ validate_nonce_args, validate_receiver_args, ) +from multiversx_sdk_cli.config import MxpyConfig from multiversx_sdk_cli.validators import ValidatorsController @@ -218,6 +219,9 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def do_unstake(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( @@ -246,6 +250,9 @@ def do_unstake(args: Any): def do_unjail(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( @@ -274,6 +281,9 @@ def do_unjail(args: Any): def do_unbond(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( @@ -302,6 +312,9 @@ def do_unbond(args: Any): def change_reward_address(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( @@ -330,6 +343,9 @@ def change_reward_address(args: Any): def do_claim(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( @@ -356,6 +372,9 @@ def do_claim(args: Any): def do_unstake_nodes(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( @@ -385,6 +404,9 @@ def do_unstake_nodes(args: Any): def do_unstake_tokens(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( @@ -413,6 +435,9 @@ def do_unstake_tokens(args: Any): def do_unbond_nodes(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( @@ -442,6 +467,9 @@ def do_unbond_nodes(args: Any): def do_unbond_tokens(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( @@ -470,6 +498,9 @@ def do_unbond_tokens(args: Any): def do_clean_registered_data(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( @@ -496,6 +527,9 @@ def do_clean_registered_data(args: Any): def do_restake_unstaked_nodes(args: Any): + cli_config = MxpyConfig.from_active_config() + cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + validate_args(args) sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index e996f98d..ceff70bc 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -1,4 +1,5 @@ import os +from dataclasses import dataclass from pathlib import Path from typing import Any @@ -141,7 +142,7 @@ def get_defaults() -> dict[str, Any]: "default_address_hrp": "erd", "proxy_url": "", "explorer_url": "", - "ask_confirmation": "true", + "ask_confirmation": "false", } @@ -199,3 +200,20 @@ def get_dependency_parent_directory(key: str) -> Path: def get_config_for_network_providers() -> NetworkProviderConfig: return NetworkProviderConfig(client_name="mxpy") + + +@dataclass +class MxpyConfig: + address_hrp: str + proxy_url: str + explorer_url: str + ask_confirmation: bool + + @classmethod + def from_active_config(cls) -> "MxpyConfig": + return cls( + address_hrp=get_address_hrp(), + proxy_url=get_proxy_url(), + explorer_url=get_explorer_url(), + ask_confirmation=get_confirmation_setting(), + ) diff --git a/multiversx_sdk_cli/utils.py b/multiversx_sdk_cli/utils.py index f15518a4..9aaedd2d 100644 --- a/multiversx_sdk_cli/utils.py +++ b/multiversx_sdk_cli/utils.py @@ -150,22 +150,36 @@ def is_arg_present(args: list[str], key: str) -> bool: return False -def log_explorer(chain: str, name: str, path: str, details: str): - networks = { - "1": ("MultiversX Mainnet Explorer", "https://explorer.multiversx.com"), - "T": ("MultiversX Testnet Explorer", "https://testnet-explorer.multiversx.com"), - "D": ("MultiversX Devnet Explorer", "https://devnet-explorer.multiversx.com"), - } - try: - explorer_name, explorer_url = networks[chain] - logger.info(f"View this {name} in the {explorer_name}: {explorer_url}/{path}/{details}") - except KeyError: - return +def log_explorer(name: str, explorer: str, path: str, details: str): + logger.info(f"View this {name} in the MultiversX Explorer: {explorer}/{path}/{details}") + + +def log_explorer_contract_address(chain: str, address: str, explorer_url: Optional[str] = ""): + if explorer_url: + log_explorer("contract address", explorer_url, "accounts", address) + else: + explorer = get_explorer_by_chain_id(chain_id=chain) + if explorer: + log_explorer("contract address", explorer, "accounts", address) -def log_explorer_contract_address(chain: str, address: str): - log_explorer(chain, "contract address", "accounts", address) +def log_explorer_transaction(chain: str, transaction_hash: str, explorer_url: Optional[str] = ""): + if explorer_url: + log_explorer("transaction", explorer_url, "transactions", transaction_hash) + else: + explorer = get_explorer_by_chain_id(chain_id=chain) + if explorer: + log_explorer("transaction", explorer, "transactions", transaction_hash) -def log_explorer_transaction(chain: str, transaction_hash: str): - log_explorer(chain, "transaction", "transactions", transaction_hash) +def get_explorer_by_chain_id(chain_id: str) -> str: + explorers_by_chain_id = { + "1": "https://explorer.multiversx.com", + "T": "https://testnet-explorer.multiversx.com", + "D": "https://devnet-explorer.multiversx.com", + } + + try: + return explorers_by_chain_id[chain_id] + except KeyError: + return "" From 827cd350a12856622e3559f468f9e496927b4d25 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 27 May 2025 14:02:14 +0300 Subject: [PATCH 12/64] update gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 50374163..ca36e556 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ venv.bak/ typings multiversx_sdk_cli/tests/testdata-out + +.DS_store From e118b945981aad5657e02558915ca09b0ea0311b Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 27 May 2025 14:35:49 +0300 Subject: [PATCH 13/64] require python 3.10 or greater --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a7b38afc..a6f06f85 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ authors = [ license = "MIT" description = "MultiversX Smart Contracts Tools" readme = "README.md" -requires-python = ">=3.8" +requires-python = ">=3.10" classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", From b7d1ff31de8617dd5e9f3a94877db0f826e29798 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 27 May 2025 16:16:21 +0300 Subject: [PATCH 14/64] add caching --- multiversx_sdk_cli/cli_shared.py | 3 +++ multiversx_sdk_cli/config.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index eb7ac39f..53566d46 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -4,6 +4,7 @@ import logging import sys from argparse import FileType +from functools import cache from pathlib import Path from typing import Any, Text, Union, cast @@ -507,6 +508,7 @@ def get_current_nonce_for_address(address: Address, proxy_url: Union[str, None]) return proxy.get_account(address).nonce +@cache def get_chain_id(chain_id: str, proxy_url: str) -> str: if chain_id and proxy_url: fetched_chain_id = _fetch_chain_id(proxy_url) @@ -523,6 +525,7 @@ def get_chain_id(chain_id: str, proxy_url: str) -> str: return _fetch_chain_id(proxy_url) +@cache def _fetch_chain_id(proxy_url: str) -> str: network_provider_config = config.get_config_for_network_providers() proxy = ProxyNetworkProvider(url=proxy_url, config=network_provider_config) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index ceff70bc..9afd8db4 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -1,5 +1,6 @@ import os from dataclasses import dataclass +from functools import cache from pathlib import Path from typing import Any @@ -32,6 +33,7 @@ def get_dependency_url(key: str, tag: str, platform: str) -> str: return url_template.replace("{TAG}", tag) +@cache def get_value(name: str) -> str: _guard_valid_name(name) data = get_active() @@ -41,6 +43,7 @@ def get_value(name: str) -> str: return value +@cache def get_address_hrp() -> str: return get_value("default_address_hrp") @@ -146,14 +149,17 @@ def get_defaults() -> dict[str, Any]: } +@cache def get_proxy_url() -> str: return get_value("proxy_url") +@cache def get_explorer_url() -> str: return get_value("explorer_url") +@cache def get_confirmation_setting() -> bool: confirmation_value = get_value("ask_confirmation") if confirmation_value.lower() in ["true", "yes", "1"]: @@ -176,6 +182,7 @@ def resolve_config_path() -> Path: return GLOBAL_CONFIG_PATH +@cache def read_file() -> dict[str, Any]: config_path = resolve_config_path() if config_path.exists(): @@ -210,6 +217,7 @@ class MxpyConfig: ask_confirmation: bool @classmethod + @cache def from_active_config(cls) -> "MxpyConfig": return cls( address_hrp=get_address_hrp(), From e066bc9ba767f514b15e0e03f344a6d90a27bf67 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 27 May 2025 16:35:50 +0300 Subject: [PATCH 15/64] pass explorer url when logging transactions --- multiversx_sdk_cli/cli_contracts.py | 2 +- multiversx_sdk_cli/cli_shared.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 825622cc..f442fe18 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -420,7 +420,7 @@ def deploy(args: Any): contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=tx.nonce) logger.info("Contract address: %s", contract_address.to_bech32()) - utils.log_explorer_contract_address(args.chain, contract_address.to_bech32()) + utils.log_explorer_contract_address(args.chain, contract_address.to_bech32(), cli_config.explorer_url) _send_or_simulate(tx, contract_address, args) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 53566d46..5bf89541 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -591,6 +591,7 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL log_explorer_transaction( chain=output_transaction["emittedTransaction"]["chainID"], transaction_hash=output_transaction["emittedTransactionHash"], + explorer_url=cli_config.explorer_url, ) return output_builder From 2906430f09b76b300ba81a448677021dc70d5fcb Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 28 May 2025 14:06:39 +0300 Subject: [PATCH 16/64] fixes --- multiversx_sdk_cli/cli_contracts.py | 6 +++--- multiversx_sdk_cli/cli_delegation.py | 2 +- multiversx_sdk_cli/cli_shared.py | 21 +++++++------------ multiversx_sdk_cli/cli_transactions.py | 2 +- multiversx_sdk_cli/cli_validators.py | 2 +- multiversx_sdk_cli/dns.py | 2 +- .../tests/test_cli_contracts.py | 1 + 7 files changed, 16 insertions(+), 20 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index f442fe18..e1649dee 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -390,7 +390,7 @@ def deploy(args: Any): args=args, ) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + chain_id = cli_shared.get_chain_id(args.proxy, args.chain) config = TransactionsFactoryConfig(chain_id) abi = Abi.load(Path(args.abi)) if args.abi else None @@ -442,7 +442,7 @@ def call(args: Any): args=args, ) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + chain_id = cli_shared.get_chain_id(args.proxy, args.chain) config = TransactionsFactoryConfig(chain_id) abi = Abi.load(Path(args.abi)) if args.abi else None @@ -487,7 +487,7 @@ def upgrade(args: Any): args=args, ) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + chain_id = cli_shared.get_chain_id(args.proxy, args.chain) config = TransactionsFactoryConfig(chain_id) abi = Abi.load(Path(args.abi)) if args.abi else None diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 973ca898..d541121f 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -395,7 +395,7 @@ def validate_arguments(args: Any): def _get_delegation_controller(args: Any): - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + chain_id = cli_shared.get_chain_id(args.proxy, args.chain) config = TransactionsFactoryConfig(chain_id) delegation = DelegationOperations(config) return delegation diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 5bf89541..e50561f8 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -6,7 +6,7 @@ from argparse import FileType from functools import cache from pathlib import Path -from typing import Any, Text, Union, cast +from typing import Any, Optional, Text, Union, cast from multiversx_sdk import ( Account, @@ -39,7 +39,7 @@ from multiversx_sdk_cli.simulation import Simulator from multiversx_sdk_cli.transactions import send_and_wait_for_result from multiversx_sdk_cli.utils import log_explorer_transaction -from multiversx_sdk_cli.ux import confirm_continuation, show_warning +from multiversx_sdk_cli.ux import confirm_continuation logger = logging.getLogger("cli_shared") @@ -509,16 +509,8 @@ def get_current_nonce_for_address(address: Address, proxy_url: Union[str, None]) @cache -def get_chain_id(chain_id: str, proxy_url: str) -> str: - if chain_id and proxy_url: - fetched_chain_id = _fetch_chain_id(proxy_url) - - if chain_id != fetched_chain_id: - show_warning( - f"The chain ID you have provided does not match the chain ID you got from the proxy. Will use the proxy's value: '{fetched_chain_id}'" - ) - return fetched_chain_id - +def get_chain_id(proxy_url: str, chain_id: Optional[str] = None) -> str: + """We know and have already validated that if chainID is not provided, proxy is provided.""" if chain_id: return chain_id @@ -647,8 +639,11 @@ def prepare_guardian_relayer_data(args: Any) -> GuardianRelayerData: def set_proxy_from_config_if_not_provided(args: Any, config: config.MxpyConfig) -> None: - """This function modifies the `args` object by setting the proxy from the config if not already set.""" + """This function modifies the `args` object by setting the proxy from the config if not already set. If proxy is not needed (chainID and nonce are provided), the proxy will not be set.""" if not args.proxy: + if hasattr(args, "chain") and args.chain and hasattr(args, "nonce") and args.nonce: + return + if config.proxy_url: logger.info(f"Using proxy URL from config: {config.proxy_url}") args.proxy = config.proxy_url diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index e3b96de0..439bb1c3 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -138,7 +138,7 @@ def create_transaction(args: Any): transfers = getattr(args, "token_transfers", None) transfers = prepare_token_transfers(transfers) if transfers else None - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + chain_id = cli_shared.get_chain_id(args.proxy, args.chain) tx_controller = TransactionsController(chain_id) tx = tx_controller.create_transaction( diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 5087e0a9..8ab154e0 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -198,7 +198,7 @@ def do_stake(args: Any): def _get_validators_controller(args: Any): - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + chain_id = cli_shared.get_chain_id(args.proxy, args.chain) validators = ValidatorsController(chain_id) return validators diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 95ab0a85..2104a3a9 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -77,7 +77,7 @@ def register(args: Any): receiver = dns_address_for_name(args.name) data = dns_register_data(args.name) - chain_id = cli_shared.get_chain_id(args.chain, args.proxy) + chain_id = cli_shared.get_chain_id(args.proxy, args.chain) controller = TransactionsController(chain_id) tx = controller.create_transaction( diff --git a/multiversx_sdk_cli/tests/test_cli_contracts.py b/multiversx_sdk_cli/tests/test_cli_contracts.py index 4d579cfe..fa9d5bf6 100644 --- a/multiversx_sdk_cli/tests/test_cli_contracts.py +++ b/multiversx_sdk_cli/tests/test_cli_contracts.py @@ -270,6 +270,7 @@ def test_contract_flow(capsys: Any): def test_contract_deploy_without_required_arguments(): + """This test passes with an unaltered config. If proxy is set in the config, the test will fail due to mxpy fetching the nonce and the chain ID.""" alice = f"{parent}/testdata/alice.pem" adder = f"{parent}/testdata/adder.wasm" From f86e7095d9c389b7c1edef8de45ccf79e301680e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 28 May 2025 15:48:51 +0300 Subject: [PATCH 17/64] fixes after review --- .../base_transactions_controller.py | 2 +- multiversx_sdk_cli/cli_contracts.py | 32 +------- multiversx_sdk_cli/cli_multisig.py | 78 ++++++------------- multiversx_sdk_cli/cli_shared.py | 33 +++++++- multiversx_sdk_cli/contracts.py | 8 +- multiversx_sdk_cli/multisig.py | 10 +-- multiversx_sdk_cli/tests/test_contracts.py | 2 +- multiversx_sdk_cli/tests/test_shared.py | 33 ++++++++ 8 files changed, 102 insertions(+), 96 deletions(-) create mode 100644 multiversx_sdk_cli/tests/test_shared.py diff --git a/multiversx_sdk_cli/base_transactions_controller.py b/multiversx_sdk_cli/base_transactions_controller.py index ce3ff4ba..49a6fb62 100644 --- a/multiversx_sdk_cli/base_transactions_controller.py +++ b/multiversx_sdk_cli/base_transactions_controller.py @@ -103,7 +103,7 @@ def _sign_relayed_transaction_if_relayer(self, transaction: Transaction, relayer if relayer and transaction.relayer: transaction.relayer_signature = relayer.sign_transaction(transaction) - def _prepare_args_for_factory(self, arguments: list[str]) -> list[Any]: + def _convert_args_to_typed_values(self, arguments: list[str]) -> list[Any]: args: list[Any] = [] for arg in arguments: diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 861970e4..eaaf2ad9 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -53,7 +53,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: ) _add_bytecode_arg(sub) _add_contract_abi_arg(sub) - _add_metadata_arg(sub) + cli_shared.add_metadata_arg(sub) cli_shared.add_outfile_arg(sub) cli_shared.add_wallet_args(args, sub) cli_shared.add_proxy_arg(sub) @@ -118,7 +118,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: _add_contract_abi_arg(sub) cli_shared.add_outfile_arg(sub) _add_bytecode_arg(sub) - _add_metadata_arg(sub) + cli_shared.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) @@ -304,34 +304,6 @@ def _add_arguments_arg(sub: Any): ) -def _add_metadata_arg(sub: Any): - sub.add_argument( - "--metadata-not-upgradeable", - dest="metadata_upgradeable", - action="store_false", - help="‼ mark the contract as NOT upgradeable (default: upgradeable)", - ) - sub.add_argument( - "--metadata-not-readable", - dest="metadata_readable", - action="store_false", - help="‼ mark the contract as NOT readable (default: readable)", - ) - sub.add_argument( - "--metadata-payable", - dest="metadata_payable", - action="store_true", - help="‼ mark the contract as payable (default: not payable)", - ) - sub.add_argument( - "--metadata-payable-by-sc", - dest="metadata_payable_by_sc", - action="store_true", - help="‼ mark the contract as payable by SC (default: not payable by SC)", - ) - sub.set_defaults(metadata_upgradeable=True, metadata_payable=False) - - def build(args: Any): message = """This command cannot build smart contracts anymore. diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index 0fd24726..6f904bf5 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -76,7 +76,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: type=str, help="the bech32 addresses of the board members", ) - _add_metadata_arg(sub) + cli_shared.add_metadata_arg(sub) cli_shared.add_outfile_arg(sub) cli_shared.add_wallet_args(args, sub) cli_shared.add_proxy_arg(sub) @@ -91,7 +91,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -122,7 +122,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -153,7 +153,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -190,7 +190,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -226,7 +226,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -262,7 +262,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -298,7 +298,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -334,7 +334,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -372,7 +372,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -411,7 +411,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -450,7 +450,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -470,7 +470,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument("--contract-to-copy", required=True, type=str, help="the bech32 address of the contract to copy") sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to copy") _add_arguments_arg(sub) - _add_metadata_arg(sub) + cli_shared.add_metadata_arg(sub) cli_shared.add_outfile_arg(sub) cli_shared.add_wallet_args(args, sub) cli_shared.add_proxy_arg(sub) @@ -485,7 +485,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -508,7 +508,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument("--contract-to-copy", required=True, type=str, help="the bech32 address of the contract to copy") sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to copy") _add_arguments_arg(sub) - _add_metadata_arg(sub) + cli_shared.add_metadata_arg(sub) cli_shared.add_outfile_arg(sub) cli_shared.add_wallet_args(args, sub) cli_shared.add_proxy_arg(sub) @@ -523,7 +523,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -555,7 +555,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -587,7 +587,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -619,7 +619,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -651,7 +651,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -683,7 +683,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -715,7 +715,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -753,7 +753,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -785,7 +785,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -817,7 +817,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--timeout", default=100, - help="max num of seconds to wait for result" " - only valid if --wait-result is set", + help="max num of seconds to wait for result - only valid if --wait-result is set", ) cli_shared.add_broadcast_args(sub) cli_shared.add_guardian_wallet_args(args, sub) @@ -1075,34 +1075,6 @@ def _add_action_id_arg(sub: Any): sub.add_argument("--action", required=True, type=int, help="the id of the action") -def _add_metadata_arg(sub: Any): - sub.add_argument( - "--metadata-not-upgradeable", - dest="metadata_upgradeable", - action="store_false", - help="‼ mark the contract as NOT upgradeable (default: upgradeable)", - ) - sub.add_argument( - "--metadata-not-readable", - dest="metadata_readable", - action="store_false", - help="‼ mark the contract as NOT readable (default: readable)", - ) - sub.add_argument( - "--metadata-payable", - dest="metadata_payable", - action="store_true", - help="‼ mark the contract as payable (default: not payable)", - ) - sub.add_argument( - "--metadata-payable-by-sc", - dest="metadata_payable_by_sc", - action="store_true", - help="‼ mark the contract as payable by SC (default: not payable by SC)", - ) - sub.set_defaults(metadata_upgradeable=True, metadata_payable=False) - - def _add_arguments_arg(sub: Any): sub.add_argument( "--arguments", diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 91df6223..13c88b54 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -282,6 +282,34 @@ def add_token_transfers_args(sub: Any): ) +def add_metadata_arg(sub: Any): + sub.add_argument( + "--metadata-not-upgradeable", + dest="metadata_upgradeable", + action="store_false", + help="‼ mark the contract as NOT upgradeable (default: upgradeable)", + ) + sub.add_argument( + "--metadata-not-readable", + dest="metadata_readable", + action="store_false", + help="‼ mark the contract as NOT readable (default: readable)", + ) + sub.add_argument( + "--metadata-payable", + dest="metadata_payable", + action="store_true", + help="‼ mark the contract as payable (default: not payable)", + ) + sub.add_argument( + "--metadata-payable-by-sc", + dest="metadata_payable_by_sc", + action="store_true", + help="‼ mark the contract as payable by SC (default: not payable by SC)", + ) + sub.set_defaults(metadata_upgradeable=True, metadata_payable=False) + + def parse_omit_fields_arg(args: Any) -> list[str]: literal = args.omit_fields parsed = ast.literal_eval(literal) @@ -628,9 +656,10 @@ def prepare_token_transfers(transfers: list[str]) -> list[TokenTransfer]: token_transfers: list[TokenTransfer] = [] for i in range(0, len(transfers) - 1, 2): - identifier = transfers[i] + extended_identifier = transfers[i] amount = int(transfers[i + 1]) - nonce = token_computer.extract_nonce_from_extended_identifier(identifier) + nonce = token_computer.extract_nonce_from_extended_identifier(extended_identifier) + identifier = token_computer.extract_identifier_from_extended_identifier(extended_identifier) token = Token(identifier, nonce) transfer = TokenTransfer(token, amount) diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index f765937e..c3a47597 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -62,7 +62,7 @@ def prepare_deploy_transaction( ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: - args = self._prepare_args_for_factory(args) + args = self._convert_args_to_typed_values(args) tx = self._factory.create_transaction_for_deploy( sender=owner.address, @@ -111,7 +111,7 @@ def prepare_execute_transaction( ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: - args = self._prepare_args_for_factory(args) + args = self._convert_args_to_typed_values(args) tx = self._factory.create_transaction_for_execute( sender=caller.address, @@ -161,7 +161,7 @@ def prepare_upgrade_transaction( ) -> Transaction: args = arguments if arguments else [] if should_prepare_args: - args = self._prepare_args_for_factory(args) + args = self._convert_args_to_typed_values(args) tx = self._factory.create_transaction_for_upgrade( sender=owner.address, @@ -203,7 +203,7 @@ def query_contract( ) -> list[Any]: args = arguments if arguments else [] if should_prepare_args: - args = self._prepare_args_for_factory(args) + args = self._convert_args_to_typed_values(args) sc_query_controller = SmartContractController(self._config.chain_id, proxy, self._abi) diff --git a/multiversx_sdk_cli/multisig.py b/multiversx_sdk_cli/multisig.py index d59cd7cb..be28cfa8 100644 --- a/multiversx_sdk_cli/multisig.py +++ b/multiversx_sdk_cli/multisig.py @@ -342,7 +342,7 @@ def prepare_transfer_execute_transaction( ) -> Transaction: args = arguments if arguments else [] if should_prepare_args_for_factory: - args = self._prepare_args_for_factory(args) + args = self._convert_args_to_typed_values(args) tx = self._factory.create_transaction_for_propose_transfer_execute( sender=owner.address, @@ -393,7 +393,7 @@ def prepare_transfer_execute_esdt_transaction( ) -> Transaction: args = arguments if arguments else [] if should_prepare_args_for_factory: - args = self._prepare_args_for_factory(args) + args = self._convert_args_to_typed_values(args) tx = self._factory.create_transaction_for_propose_transfer_esdt_execute( sender=owner.address, @@ -445,7 +445,7 @@ def prepare_async_call_transaction( ) -> Transaction: args = arguments if arguments else [] if should_prepare_args_for_factory: - args = self._prepare_args_for_factory(args) + args = self._convert_args_to_typed_values(args) tx = self._factory.create_transaction_for_propose_async_call( sender=owner.address, @@ -499,7 +499,7 @@ def prepare_contract_deploy_from_source_transaction( ) -> Transaction: args = arguments if arguments else [] if should_prepare_args_for_factory: - args = self._prepare_args_for_factory(args) + args = self._convert_args_to_typed_values(args) tx = self._factory.create_transaction_for_propose_contract_deploy_from_source( sender=owner.address, @@ -555,7 +555,7 @@ def prepare_contract_upgrade_from_source_transaction( ) -> Transaction: args = arguments if arguments else [] if should_prepare_args_for_factory: - args = self._prepare_args_for_factory(args) + args = self._convert_args_to_typed_values(args) tx = self._factory.create_transaction_for_propose_contract_upgrade_from_source( sender=owner.address, diff --git a/multiversx_sdk_cli/tests/test_contracts.py b/multiversx_sdk_cli/tests/test_contracts.py index d81cad3f..0ad046d6 100644 --- a/multiversx_sdk_cli/tests/test_contracts.py +++ b/multiversx_sdk_cli/tests/test_contracts.py @@ -40,7 +40,7 @@ def test_prepare_args_for_factories(): "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", ] - arguments = sc._prepare_args_for_factory(args) + arguments = sc._convert_args_to_typed_values(args) assert arguments[0].get_payload() == b"\x05" assert arguments[1].get_payload() == 123 assert arguments[2].get_payload() is False diff --git a/multiversx_sdk_cli/tests/test_shared.py b/multiversx_sdk_cli/tests/test_shared.py new file mode 100644 index 00000000..53c8b21d --- /dev/null +++ b/multiversx_sdk_cli/tests/test_shared.py @@ -0,0 +1,33 @@ +from multiversx_sdk_cli.cli_shared import prepare_token_transfers + + +def test_prepare_token_tranfers(): + # list of token transfers as interpreted by the CLI + token_transfers = [ + "FNG-123456", + "10000", + "SFT-123123-0a", + "3", + "NFT-987654-07", + "1", + "META-777777-10", + "123456789", + ] + transfers = prepare_token_transfers(token_transfers) + + assert len(transfers) == 4 + assert transfers[0].token.identifier == "FNG-123456" + assert transfers[0].token.nonce == 0 + assert transfers[0].amount == 10000 + + assert transfers[1].token.identifier == "SFT-123123" + assert transfers[1].token.nonce == 10 + assert transfers[1].amount == 3 + + assert transfers[2].token.identifier == "NFT-987654" + assert transfers[2].token.nonce == 7 + assert transfers[2].amount == 1 + + assert transfers[3].token.identifier == "META-777777" + assert transfers[3].token.nonce == 16 + assert transfers[3].amount == 123456789 From bf2f946014eb43c4a9e831f19c486085fb795527 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 28 May 2025 17:10:28 +0300 Subject: [PATCH 18/64] fix mypy error --- multiversx_sdk_cli/cli_shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index e50561f8..7c31ac74 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -558,7 +558,7 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL outfile = args.outfile if hasattr(args, "outfile") else None cli_config = config.MxpyConfig.from_active_config() - hash = "" + hash = b"" try: if send_wait_result: _confirm_continuation_if_required(cli_config, tx) From 4e029f28f8c17829819a3b539b44cc4e97132be9 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 29 May 2025 17:51:23 +0300 Subject: [PATCH 19/64] move env to separate env file --- CLI.md | 181 ++++++++++++++++++++++-- CLI.md.sh | 10 ++ multiversx_sdk_cli/cli.py | 5 +- multiversx_sdk_cli/cli_contracts.py | 11 +- multiversx_sdk_cli/cli_delegation.py | 41 +++--- multiversx_sdk_cli/cli_dns.py | 11 +- multiversx_sdk_cli/cli_env.py | 149 ++++++++++++++++++++ multiversx_sdk_cli/cli_shared.py | 17 +-- multiversx_sdk_cli/cli_transactions.py | 11 +- multiversx_sdk_cli/cli_validators.py | 24 ++-- multiversx_sdk_cli/cli_wallet.py | 2 +- multiversx_sdk_cli/config.py | 49 ------- multiversx_sdk_cli/contracts.py | 2 +- multiversx_sdk_cli/delegation.py | 2 +- multiversx_sdk_cli/dns.py | 2 +- multiversx_sdk_cli/env.py | 182 +++++++++++++++++++++++++ multiversx_sdk_cli/errors.py | 20 +++ 17 files changed, 596 insertions(+), 123 deletions(-) create mode 100644 multiversx_sdk_cli/cli_env.py create mode 100644 multiversx_sdk_cli/env.py diff --git a/CLI.md b/CLI.md index 79de3032..2cf2143a 100644 --- a/CLI.md +++ b/CLI.md @@ -23,7 +23,7 @@ See: COMMAND GROUPS: - {contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet} + {contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet,env} TOP-LEVEL OPTIONS: -h, --help show this help message and exit @@ -40,12 +40,13 @@ ledger Get Ledger App addresses and version wallet Create wallet, derive secret key from mnemonic, bech32 address helpers etc. validator-wallet Create a validator wallet, sign and verify messages and convert a validator wallet to a hex secret key. deps Manage dependencies or multiversx-sdk modules -config Configure multiversx-sdk (default values etc.) +config Configure MultiversX CLI (default values etc.) 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 +env Configure MultiversX CLI to use specific environment values. ``` ## Group **Contract** @@ -421,6 +422,7 @@ options: or Ledger devices (default: 0) --sender-username SENDER_USERNAME 🖄 the username of the sender --hrp HRP The hrp used to convert the address to its bech32 representation + --skip-confirmation, -y can be used to skip the confirmation prompt ``` ### Contract.ReproducibleBuild @@ -2830,7 +2832,7 @@ options: $ mxpy config --help usage: mxpy config COMMAND [-h] ... -Configure multiversx-sdk (default values etc.) +Configure MultiversX CLI (default values etc.) COMMANDS: {dump,get,set,delete,new,switch,list,reset} @@ -2841,12 +2843,12 @@ OPTIONS: ---------------- COMMANDS summary ---------------- -dump Dumps configuration. -get Gets a configuration value. -set Sets a configuration value. -delete Deletes a configuration value. -new Creates a new configuration. -switch Switch to a different config +dump Dumps the active configuration. +get Gets a configuration value from the active configuration. +set Sets a configuration value for the active configuration. +delete Deletes a configuration value from the active configuration. +new Creates a new configuration and sets it as the active configuration. +switch Switch to a different config. list List available configs reset Deletes the config file. Default config will be used. @@ -2858,7 +2860,7 @@ reset Deletes the config file. Default config will be u $ mxpy config dump --help usage: mxpy config dump [-h] ... -Dumps configuration. +Dumps the active configuration. options: -h, --help show this help message and exit @@ -2872,7 +2874,7 @@ options: $ mxpy config get --help usage: mxpy config get [-h] ... -Gets a configuration value. +Gets a configuration value from the active configuration. positional arguments: name the name of the configuration entry @@ -2888,7 +2890,7 @@ options: $ mxpy config set --help usage: mxpy config set [-h] ... -Sets a configuration value. +Sets a configuration value for the active configuration. positional arguments: name the name of the configuration entry @@ -2905,7 +2907,7 @@ options: $ mxpy config new --help usage: mxpy config new [-h] ... -Creates a new configuration. +Creates a new configuration and sets it as the active configuration. positional arguments: name the name of the configuration entry @@ -2922,7 +2924,7 @@ options: $ mxpy config switch --help usage: mxpy config switch [-h] ... -Switch to a different config +Switch to a different config. positional arguments: name the name of the configuration entry @@ -3074,3 +3076,154 @@ options: --wallet-url WALLET_URL custom wallet url to call the faucet from ``` +## Group **Environment** + + +``` +$ mxpy env --help +usage: mxpy env COMMAND [-h] ... + +Configure MultiversX CLI to use specific environment values. + +COMMANDS: + {new,get,set,dump,delete,switch,list,remove,reset} + +OPTIONS: + -h, --help show this help message and exit + +---------------- +COMMANDS summary +---------------- +new Creates a new environment and sets it as the active environment. +get Gets an env value from the active environment. +set Sets an env value for the active environment. +dump Dumps the active environment. +delete Deletes an env value from the active environment. +switch Switch to a different environment. +list List available environments +remove Deletes an environment from the env file. Will switch to default env. +reset Deletes the environment file. Default env will be used. + +``` +### Environment.New + + +``` +$ mxpy env new --help +usage: mxpy env new [-h] ... + +Creates a new environment and sets it as the active environment. + +positional arguments: + name the name of the configuration entry + +options: + -h, --help show this help message and exit + --template TEMPLATE an environment from which to create the new environment + +``` +### Environment.Set + + +``` +$ mxpy env set --help +usage: mxpy env set [-h] ... + +Sets an env value for the active environment. + +positional arguments: + name the name of the configuration entry + value the new value + +options: + -h, --help show this help message and exit + +``` +### Environment.Get + + +``` +$ mxpy env get --help +usage: mxpy env get [-h] ... + +Gets an env value from the active environment. + +positional arguments: + name the name of the configuration entry + +options: + -h, --help show this help message and exit + +``` +### Environment.Dump + + +``` +$ mxpy env dump --help +usage: mxpy env dump [-h] ... + +Dumps the active environment. + +options: + -h, --help show this help message and exit + --default dumps the default environment instead of the active one. + +``` +### Environment.Switch + + +``` +$ mxpy env switch --help +usage: mxpy env switch [-h] ... + +Switch to a different environment. + +positional arguments: + name the name of the configuration entry + +options: + -h, --help show this help message and exit + +``` +### Environment.List + + +``` +$ mxpy env list --help +usage: mxpy env list [-h] ... + +List available environments + +options: + -h, --help show this help message and exit + +``` +### Environment.Remove + + +``` +$ mxpy env remove --help +usage: mxpy env remove [-h] ... + +Deletes an environment from the env file. Will switch to default env. + +positional arguments: + environment The environment to remove from env file. + +options: + -h, --help show this help message and exit + +``` +### Environment.Reset + + +``` +$ mxpy env reset --help +usage: mxpy env reset [-h] ... + +Deletes the environment file. Default env will be used. + +options: + -h, --help show this help message and exit + +``` diff --git a/CLI.md.sh b/CLI.md.sh index 46ebfb16..e40cf91e 100755 --- a/CLI.md.sh +++ b/CLI.md.sh @@ -130,6 +130,16 @@ generate() { group "Faucet" "faucet" command "Faucet.Request" "faucet request" + + group "Environment" "env" + command "Environment.New" "env new" + command "Environment.Set" "env set" + command "Environment.Get" "env get" + command "Environment.Dump" "env dump" + command "Environment.Switch" "env switch" + command "Environment.List" "env list" + command "Environment.Remove" "env remove" + command "Environment.Reset" "env reset" } generate diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 6876d719..3e21b209 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -15,6 +15,7 @@ import multiversx_sdk_cli.cli_delegation import multiversx_sdk_cli.cli_deps import multiversx_sdk_cli.cli_dns +import multiversx_sdk_cli.cli_env import multiversx_sdk_cli.cli_faucet import multiversx_sdk_cli.cli_ledger import multiversx_sdk_cli.cli_localnet @@ -24,6 +25,7 @@ import multiversx_sdk_cli.cli_wallet import multiversx_sdk_cli.version from multiversx_sdk_cli import config, errors, utils, ux +from multiversx_sdk_cli.env import get_address_hrp logger = logging.getLogger("cli") @@ -64,7 +66,7 @@ def _do_main(cli_args: list[str]): ) verify_deprecated_entries_in_config_file() - default_hrp = config.get_address_hrp() + default_hrp = get_address_hrp() LibraryConfig.default_address_hrp = default_hrp if hasattr(args, "recall_nonce") and args.recall_nonce: @@ -123,6 +125,7 @@ def setup_parser(args: list[str]): 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)) + commands.append(multiversx_sdk_cli.cli_env.setup_parser(subparsers)) parser.epilog = """ ---------------------- diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index e1649dee..38c5cb70 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -25,11 +25,12 @@ validate_transaction_args, ) from multiversx_sdk_cli.cli_output import CLIOutputBuilder -from multiversx_sdk_cli.config import MxpyConfig, get_config_for_network_providers +from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS from multiversx_sdk_cli.contract_verification import trigger_contract_verification from multiversx_sdk_cli.contracts import SmartContract from multiversx_sdk_cli.docker import is_docker_installed, run_docker +from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.errors import DockerMissingError from multiversx_sdk_cli.ux import show_warning @@ -376,7 +377,7 @@ def build(args: Any): def deploy(args: Any): logger.debug("deploy") - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_transaction_args(args) @@ -428,7 +429,7 @@ def deploy(args: Any): def call(args: Any): logger.debug("call") - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_transaction_args(args) @@ -473,7 +474,7 @@ def call(args: Any): def upgrade(args: Any): logger.debug("upgrade") - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_transaction_args(args) @@ -521,7 +522,7 @@ def upgrade(args: Any): def query(args: Any): logger.debug("query") - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_proxy_argument(args) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index d541121f..bba9ae6b 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -19,8 +19,9 @@ validate_proxy_argument, validate_receiver_args, ) -from multiversx_sdk_cli.config import MxpyConfig, get_config_for_network_providers +from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.delegation import DelegationOperations +from multiversx_sdk_cli.env import MxpyEnv def setup_parser(args: list[str], subparsers: Any) -> Any: @@ -402,7 +403,7 @@ def _get_delegation_controller(args: Any): def do_create_delegation_contract(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -433,7 +434,7 @@ def do_create_delegation_contract(args: Any): def get_contract_address_by_deploy_tx_hash(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_proxy_argument(args) @@ -453,7 +454,7 @@ def get_contract_address_by_deploy_tx_hash(args: Any): def add_new_nodes(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -504,7 +505,7 @@ def _get_public_keys_and_signed_messages(args: Any) -> tuple[list[ValidatorPubli def remove_nodes(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -556,7 +557,7 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def stake_nodes(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) _check_if_either_bls_keys_or_validators_file_are_provided(args) @@ -597,7 +598,7 @@ def _check_if_either_bls_keys_or_validators_file_are_provided(args: Any): def unbond_nodes(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) _check_if_either_bls_keys_or_validators_file_are_provided(args) @@ -630,7 +631,7 @@ def unbond_nodes(args: Any): def unstake_nodes(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) _check_if_either_bls_keys_or_validators_file_are_provided(args) @@ -663,7 +664,7 @@ def unstake_nodes(args: Any): def unjail_nodes(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) _check_if_either_bls_keys_or_validators_file_are_provided(args) @@ -696,7 +697,7 @@ def unjail_nodes(args: Any): def delegate(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -727,7 +728,7 @@ def delegate(args: Any): def claim_rewards(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -756,7 +757,7 @@ def claim_rewards(args: Any): def redelegate_rewards(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -785,7 +786,7 @@ def redelegate_rewards(args: Any): def undelegate(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -816,7 +817,7 @@ def undelegate(args: Any): def withdraw(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -846,7 +847,7 @@ def withdraw(args: Any): def change_service_fee(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -877,7 +878,7 @@ def change_service_fee(args: Any): def modify_delegation_cap(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -908,7 +909,7 @@ def modify_delegation_cap(args: Any): def automatic_activation(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -940,7 +941,7 @@ def automatic_activation(args: Any): def redelegate_cap(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -972,7 +973,7 @@ def redelegate_cap(args: Any): def set_metadata(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) @@ -1005,7 +1006,7 @@ def set_metadata(args: Any): def make_new_contract_from_validator_data(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_arguments(args) diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index f028148f..010450bc 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -5,7 +5,7 @@ from rich.table import Table from multiversx_sdk_cli import cli_shared -from multiversx_sdk_cli.config import MxpyConfig, get_config_for_network_providers +from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX from multiversx_sdk_cli.dns import ( compute_dns_address_for_shard_id, @@ -17,6 +17,7 @@ validate_name, version, ) +from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.errors import ArgumentsNotProvidedError @@ -136,7 +137,7 @@ def _ensure_proxy_is_provided(args: Any): def dns_resolve(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) _ensure_proxy_is_provided(args) @@ -148,7 +149,7 @@ def dns_resolve(args: Any): def dns_validate_name(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) _ensure_proxy_is_provided(args) @@ -174,7 +175,7 @@ def get_dns_address_for_name_hex(args: Any): def get_registration_cost(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) _ensure_proxy_is_provided(args) @@ -184,7 +185,7 @@ def get_registration_cost(args: Any): def get_version(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) _ensure_proxy_is_provided(args) diff --git a/multiversx_sdk_cli/cli_env.py b/multiversx_sdk_cli/cli_env.py new file mode 100644 index 00000000..4e3614a9 --- /dev/null +++ b/multiversx_sdk_cli/cli_env.py @@ -0,0 +1,149 @@ +import logging +import os +from typing import Any + +from multiversx_sdk_cli import cli_shared +from multiversx_sdk_cli.env import ( + create_new_env, + delete_env, + delete_value, + get_active_env, + get_defaults, + get_value, + read_env_file, + resolve_env_path, + set_active, + set_value, +) +from multiversx_sdk_cli.utils import dump_out_json +from multiversx_sdk_cli.ux import confirm_continuation + +logger = logging.getLogger("cli.env") + + +def setup_parser(subparsers: Any) -> Any: + parser = cli_shared.add_group_subparser( + subparsers, "env", "Configure MultiversX CLI to use specific environment values." + ) + subparsers = parser.add_subparsers() + + sub = cli_shared.add_command_subparser( + subparsers, "env", "new", "Creates a new environment and sets it as the active environment." + ) + _add_name_arg(sub) + sub.add_argument( + "--template", + required=False, + help="an environment from which to create the new environment", + ) + sub.set_defaults(func=new_env) + + sub = cli_shared.add_command_subparser(subparsers, "env", "get", "Gets an env value from the active environment.") + _add_name_arg(sub) + sub.set_defaults(func=get_env_value) + + sub = cli_shared.add_command_subparser(subparsers, "env", "set", "Sets an env value for the active environment.") + _add_name_arg(sub) + sub.add_argument("value", type=str, help="the new value") + sub.set_defaults(func=set_env_value) + + sub = cli_shared.add_command_subparser(subparsers, "env", "dump", "Dumps the active environment.") + sub.add_argument( + "--default", + required=False, + help="dumps the default environment instead of the active one.", + action="store_true", + ) + sub.set_defaults(func=dump) + + sub = cli_shared.add_command_subparser( + subparsers, "env", "delete", "Deletes an env value from the active environment." + ) + _add_name_arg(sub) + sub.set_defaults(func=delete_env_value) + + sub = cli_shared.add_command_subparser(subparsers, "env", "switch", "Switch to a different environment.") + _add_name_arg(sub) + sub.set_defaults(func=switch_env) + + sub = cli_shared.add_command_subparser(subparsers, "env", "list", "List available environments") + sub.set_defaults(func=list_envs) + + sub = cli_shared.add_command_subparser( + subparsers, + "env", + "remove", + "Deletes an environment from the env file. Will switch to default env.", + ) + sub.add_argument("environment", type=str, help="The environment to remove from env file.") + sub.set_defaults(func=remove_env_entry) + + sub = cli_shared.add_command_subparser( + subparsers, + "env", + "reset", + "Deletes the environment file. Default env will be used.", + ) + sub.set_defaults(func=delete_env_file) + + parser.epilog = cli_shared.build_group_epilog(subparsers) + return subparsers + + +def _add_name_arg(sub: Any): + sub.add_argument("name", type=str, help="the name of the configuration entry") + + +def dump(args: Any): + if args.default: + dump_out_json(get_defaults()) + else: + dump_out_json(get_active_env()) + + +def get_env_value(args: Any): + value = get_value(args.name) + print(value) + + +def set_env_value(args: Any): + set_value(args.name, args.value) + + +def delete_env_value(args: Any): + delete_value(args.name) + + +def new_env(args: Any): + create_new_env(name=args.name, template=args.template) + dump_out_json(get_active_env()) + + +def switch_env(args: Any): + set_active(args.name) + dump_out_json(get_active_env()) + + +def list_envs(args: Any): + data = read_env_file() + dump_out_json(data) + + +def remove_env_entry(args: Any): + envs_file = resolve_env_path() + if not envs_file.is_file(): + logger.info("Environment file not found. Aborting...") + return + + delete_env(args.environment) + + +def delete_env_file(args: Any): + envs_file = resolve_env_path() + if not envs_file.is_file(): + logger.info("Environment file not found. Aborting...") + return + + confirm_continuation(f"The file `{str(envs_file)}` will be deleted. Do you want to continue? (y/n)") + os.remove(envs_file) + logger.info("Successfully deleted the environment file.") diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 7c31ac74..d09bf308 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -29,6 +29,7 @@ DEFAULT_TX_VERSION, TCS_SERVICE_ID, ) +from multiversx_sdk_cli.env import MxpyEnv, get_address_hrp from multiversx_sdk_cli.errors import ( ArgumentsNotProvidedError, BadUsage, @@ -328,7 +329,7 @@ def _get_address_hrp(args: Any) -> str: if hrp: return hrp - return config.get_address_hrp() + return get_address_hrp() def _get_hrp_from_proxy(args: Any) -> str: @@ -557,7 +558,7 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL output_builder.set_emitted_transaction(tx) outfile = args.outfile if hasattr(args, "outfile") else None - cli_config = config.MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() hash = b"" try: if send_wait_result: @@ -589,8 +590,8 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL return output_builder -def _confirm_continuation_if_required(config: config.MxpyConfig, tx: Transaction) -> None: - if config.ask_confirmation: +def _confirm_continuation_if_required(env: MxpyEnv, tx: Transaction) -> None: + if env.ask_confirmation: transaction = tx.to_dictionary() # decode the data field from base64 if it exists @@ -638,12 +639,12 @@ def prepare_guardian_relayer_data(args: Any) -> GuardianRelayerData: ) -def set_proxy_from_config_if_not_provided(args: Any, config: config.MxpyConfig) -> None: +def set_proxy_from_config_if_not_provided(args: Any, env: MxpyEnv) -> None: """This function modifies the `args` object by setting the proxy from the config if not already set. If proxy is not needed (chainID and nonce are provided), the proxy will not be set.""" if not args.proxy: if hasattr(args, "chain") and args.chain and hasattr(args, "nonce") and args.nonce: return - if config.proxy_url: - logger.info(f"Using proxy URL from config: {config.proxy_url}") - args.proxy = config.proxy_url + if env.proxy_url: + logger.info(f"Using proxy URL from config: {env.proxy_url}") + args.proxy = env.proxy_url diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 439bb1c3..14dc8222 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -23,7 +23,8 @@ ) from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.cli_output import CLIOutputBuilder -from multiversx_sdk_cli.config import MxpyConfig, get_config_for_network_providers +from multiversx_sdk_cli.config import get_config_for_network_providers +from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.errors import BadUsage, IncorrectWalletError, NoWalletProvided from multiversx_sdk_cli.transactions import ( TransactionsController, @@ -114,7 +115,7 @@ def _add_common_arguments(args: list[str], sub: Any): def create_transaction(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_nonce_args(args) @@ -175,7 +176,7 @@ def prepare_token_transfers(transfers: list[Any]) -> list[TokenTransfer]: def send_transaction(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_proxy_argument(args) @@ -197,7 +198,7 @@ def send_transaction(args: Any): def sign_transaction(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_broadcast_args(args) @@ -239,7 +240,7 @@ def sign_transaction(args: Any): def relay_transaction(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) ensure_relayer_wallet_args_are_provided(args) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 8ab154e0..9d6ebeba 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -11,7 +11,7 @@ validate_nonce_args, validate_receiver_args, ) -from multiversx_sdk_cli.config import MxpyConfig +from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.validators import ValidatorsController @@ -219,7 +219,7 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def do_unstake(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) @@ -250,7 +250,7 @@ def do_unstake(args: Any): def do_unjail(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) @@ -281,7 +281,7 @@ def do_unjail(args: Any): def do_unbond(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) @@ -312,7 +312,7 @@ def do_unbond(args: Any): def change_reward_address(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) @@ -343,7 +343,7 @@ def change_reward_address(args: Any): def do_claim(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) @@ -372,7 +372,7 @@ def do_claim(args: Any): def do_unstake_nodes(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) @@ -404,7 +404,7 @@ def do_unstake_nodes(args: Any): def do_unstake_tokens(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) @@ -435,7 +435,7 @@ def do_unstake_tokens(args: Any): def do_unbond_nodes(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) @@ -467,7 +467,7 @@ def do_unbond_nodes(args: Any): def do_unbond_tokens(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) @@ -498,7 +498,7 @@ def do_unbond_tokens(args: Any): def do_clean_registered_data(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) @@ -527,7 +527,7 @@ def do_clean_registered_data(args: Any): def do_restake_unstaked_nodes(args: Any): - cli_config = MxpyConfig.from_active_config() + cli_config = MxpyEnv.from_active_env() cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_args(args) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 558e720a..48d99048 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -9,8 +9,8 @@ from multiversx_sdk.core.address import get_shard_of_pubkey from multiversx_sdk_cli import cli_shared, utils -from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS +from multiversx_sdk_cli.env import get_address_hrp from multiversx_sdk_cli.errors import ( BadUsage, BadUserInput, diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 9afd8db4..219c7770 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -1,5 +1,4 @@ import os -from dataclasses import dataclass from functools import cache from pathlib import Path from typing import Any @@ -43,11 +42,6 @@ def get_value(name: str) -> str: return value -@cache -def get_address_hrp() -> str: - return get_value("default_address_hrp") - - def set_value(name: str, value: Any): _guard_valid_name(name) data = read_file() @@ -142,34 +136,9 @@ def get_defaults() -> dict[str, Any]: "dependencies.testwallets.urlTemplate.osx": "https://github.com/multiversx/mx-sdk-testwallets/archive/{TAG}.tar.gz", "dependencies.testwallets.urlTemplate.windows": "https://github.com/multiversx/mx-sdk-testwallets/archive/{TAG}.tar.gz", "github_api_token": "", - "default_address_hrp": "erd", - "proxy_url": "", - "explorer_url": "", - "ask_confirmation": "false", } -@cache -def get_proxy_url() -> str: - return get_value("proxy_url") - - -@cache -def get_explorer_url() -> str: - return get_value("explorer_url") - - -@cache -def get_confirmation_setting() -> bool: - confirmation_value = get_value("ask_confirmation") - if confirmation_value.lower() in ["true", "yes", "1"]: - return True - elif confirmation_value.lower() in ["false", "no", "0"]: - return False - else: - raise errors.InvalidConfirmationSettingError(confirmation_value) - - def get_deprecated_entries_in_config_file(): default_config_keys = set(get_defaults().keys()) current_config_keys = set(get_active().keys()) @@ -207,21 +176,3 @@ def get_dependency_parent_directory(key: str) -> Path: def get_config_for_network_providers() -> NetworkProviderConfig: return NetworkProviderConfig(client_name="mxpy") - - -@dataclass -class MxpyConfig: - address_hrp: str - proxy_url: str - explorer_url: str - ask_confirmation: bool - - @classmethod - @cache - def from_active_config(cls) -> "MxpyConfig": - return cls( - address_hrp=get_address_hrp(), - proxy_url=get_proxy_url(), - explorer_url=get_explorer_url(), - ask_confirmation=get_confirmation_setting(), - ) diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index f4b19f2c..f34798c7 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -27,7 +27,7 @@ from multiversx_sdk_cli import errors from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController -from multiversx_sdk_cli.config import get_address_hrp +from multiversx_sdk_cli.env import get_address_hrp from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount diff --git a/multiversx_sdk_cli/delegation.py b/multiversx_sdk_cli/delegation.py index 17a8c377..e87e2862 100644 --- a/multiversx_sdk_cli/delegation.py +++ b/multiversx_sdk_cli/delegation.py @@ -8,7 +8,7 @@ from multiversx_sdk.abi import BigUIntValue, Serializer from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController -from multiversx_sdk_cli.config import get_address_hrp +from multiversx_sdk_cli.env import get_address_hrp from multiversx_sdk_cli.errors import BadUsage from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 2104a3a9..2714ca5a 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -15,8 +15,8 @@ validate_chain_id_args, validate_transaction_args, ) -from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX +from multiversx_sdk_cli.env import get_address_hrp from multiversx_sdk_cli.transactions import TransactionsController MaxNumShards = 256 diff --git a/multiversx_sdk_cli/env.py b/multiversx_sdk_cli/env.py new file mode 100644 index 00000000..8a878268 --- /dev/null +++ b/multiversx_sdk_cli/env.py @@ -0,0 +1,182 @@ +from dataclasses import dataclass +from functools import cache +from pathlib import Path +from typing import Any + +from multiversx_sdk_cli.errors import ( + EnvironmentAlreadyExistsError, + EnvironmentProtectedError, + InvalidConfirmationSettingError, + InvalidEnvironmentValue, + UnknownEnvironmentError, +) +from multiversx_sdk_cli.utils import read_json_file, write_json_file + +SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() +LOCAL_ENV_PATH = Path("env.mxpy.json").resolve() +GLOBAL_ENV_PATH = SDK_PATH / "env.mxpy.json" + + +@dataclass +class MxpyEnv: + address_hrp: str + proxy_url: str + explorer_url: str + ask_confirmation: bool + + @classmethod + @cache + def from_active_env(cls) -> "MxpyEnv": + return cls( + address_hrp=get_address_hrp(), + proxy_url=get_proxy_url(), + explorer_url=get_explorer_url(), + ask_confirmation=get_confirmation_setting(), + ) + + +def get_defaults() -> dict[str, str]: + return { + "default_address_hrp": "erd", + "proxy_url": "", + "explorer_url": "", + "ask_confirmation": "false", + } + + +@cache +def get_address_hrp() -> str: + return get_value("default_address_hrp") + + +@cache +def get_proxy_url() -> str: + return get_value("proxy_url") + + +@cache +def get_explorer_url() -> str: + return get_value("explorer_url") + + +@cache +def get_confirmation_setting() -> bool: + confirmation_value = get_value("ask_confirmation") + if confirmation_value.lower() in ["true", "yes", "1"]: + return True + elif confirmation_value.lower() in ["false", "no", "0"]: + return False + else: + raise InvalidConfirmationSettingError(confirmation_value) + + +@cache +def get_value(name: str) -> str: + _guard_valid_name(name) + data = get_active_env() + default_value = get_defaults()[name] + value = data.get(name, default_value) + assert isinstance(value, str) + return value + + +def _guard_valid_name(name: str): + if name not in get_defaults().keys(): + raise InvalidEnvironmentValue(f"Key is not present in environment config: [{name}]") + + +def get_active_env() -> dict[str, str]: + data = read_env_file() + envs: dict[str, Any] = data.get("environments", {}) + active_env_name: str = data.get("active", "default") + result: dict[str, str] = envs.get(active_env_name, {}) + + return result + + +@cache +def read_env_file() -> dict[str, Any]: + env_path = resolve_env_path() + if env_path.exists(): + data: dict[str, Any] = read_json_file(env_path) + return data + return dict() + + +def resolve_env_path() -> Path: + if LOCAL_ENV_PATH.is_file(): + return LOCAL_ENV_PATH + return GLOBAL_ENV_PATH + + +def set_value(name: str, value: Any): + _guard_valid_name(name) + data = read_env_file() + active_env = data.get("active", "default") + data.setdefault("environments", {}) + data["environments"].setdefault(active_env, {}) + data["environments"][active_env][name] = value + write_file(data) + + +def write_file(data: dict[str, Any]): + env_path = resolve_env_path() + write_json_file(str(env_path), data) + + +def delete_value(name: str): + """Deletes a key-value pair of the active env.""" + _guard_valid_env_deletion(name) + data = read_env_file() + active_env = data.get("active", "default") + data.setdefault("environments", {}) + data["environments"].setdefault(active_env, {}) + del data["environments"][active_env][name] + write_file(data) + + +def _guard_valid_env_deletion(name: str): + if name == "default": + raise EnvironmentProtectedError(name) + + +def set_active(name: str): + data = read_env_file() + _guard_valid_env_name(data, name) + data["active"] = name + write_file(data) + + +def _guard_valid_env_name(env: Any, name: str): + envs = env.get("environments", {}) + if name not in envs: + raise UnknownEnvironmentError(name) + + +def create_new_env(name: str, template: str): + data = read_env_file() + _guard_env_unique(data, name) + new_env = {} + if template: + _guard_valid_env_name(data, template) + new_env = data["environments"][template] + + data["active"] = name + data.setdefault("environments", {}) + data["environments"][name] = new_env + write_file(data) + + +def _guard_env_unique(env: Any, name: str): + envs = env.get("environments", {}) + if name in envs: + raise EnvironmentAlreadyExistsError(name) + + +def delete_env(name: str): + _guard_valid_env_deletion(name) + data = read_env_file() + data["environments"].pop(name, None) + if data["active"] == name: + data["active"] = "default" + write_file(data) diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index eb8025d7..4e418501 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -156,3 +156,23 @@ def __init__(self, value: str): super().__init__( f"Invalid confirmation setting: {value}. Valid values are: ['true', 'false', 'yes', 'no', '1', '0']." ) + + +class InvalidEnvironmentValue(KnownError): + def __init__(self, message: str): + super().__init__(message) + + +class EnvironmentProtectedError(KnownError): + def __init__(self, name: str): + super().__init__(f"This environment name is protected: {name}.") + + +class UnknownEnvironmentError(KnownError): + def __init__(self, name: str): + super().__init__(f"Environment entry is not known: {name}.") + + +class EnvironmentAlreadyExistsError(KnownError): + def __init__(self, name: str): + super().__init__(f"Environment entry already exists: {name}.") From aadd695d9d1b8f7c02c5c352d7873cd07611f1a0 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 30 May 2025 18:20:04 +0300 Subject: [PATCH 20/64] wip: implement address config --- multiversx_sdk_cli/address.py | 137 +++++++++++++++++++++++++++ multiversx_sdk_cli/cli.py | 2 + multiversx_sdk_cli/cli_address.py | 150 ++++++++++++++++++++++++++++++ multiversx_sdk_cli/errors.py | 20 ++++ 4 files changed, 309 insertions(+) create mode 100644 multiversx_sdk_cli/address.py create mode 100644 multiversx_sdk_cli/cli_address.py diff --git a/multiversx_sdk_cli/address.py b/multiversx_sdk_cli/address.py new file mode 100644 index 00000000..1dcebcf7 --- /dev/null +++ b/multiversx_sdk_cli/address.py @@ -0,0 +1,137 @@ +from functools import cache +from pathlib import Path +from typing import Any + +from multiversx_sdk_cli.errors import ( + AliasAlreadyExistsError, + AliasProtectedError, + InvalidAddressConfigValue, + UnknownAddressAliasError, +) +from multiversx_sdk_cli.utils import read_json_file, write_json_file + +SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() +LOCAL_ADDRESS_CONFIG_PATH = Path("addresses.mxpy.json").resolve() +GLOBAL_ADDRESS_CONFIG_PATH = SDK_PATH / "addresses.mxpy.json" + + +def get_defaults() -> dict[str, str]: + return { + "kind": "", + "path": "", + "index": "", + "password": "", + "passwordPath": "", + } + + +@cache +def get_value(name: str) -> str: + _guard_valid_name(name) + data = get_active_address() + default_value = get_defaults()[name] + value = data.get(name, default_value) + assert isinstance(value, str) + return value + + +def _guard_valid_name(name: str): + if name not in get_defaults().keys(): + raise InvalidAddressConfigValue(f"Key is not present in address config: [{name}]") + + +def get_active_address() -> dict[str, str]: + data = read_address_config_file() + addresses: dict[str, Any] = data.get("addresses", {}) + active_address: str = data.get("active", "default") + result: dict[str, str] = addresses.get(active_address, {}) + + return result + + +@cache +def read_address_config_file() -> dict[str, Any]: + config_path = resolve_address_config_path() + if config_path.exists(): + data: dict[str, Any] = read_json_file(config_path) + return data + return dict() + + +def resolve_address_config_path() -> Path: + if LOCAL_ADDRESS_CONFIG_PATH.is_file(): + return LOCAL_ADDRESS_CONFIG_PATH + return GLOBAL_ADDRESS_CONFIG_PATH + + +def set_value(name: str, value: Any): + _guard_valid_name(name) + data = read_address_config_file() + active_env = data.get("active", "default") + data.setdefault("addresses", {}) + data["addresses"].setdefault(active_env, {}) + data["addresses"][active_env][name] = value + write_file(data) + + +def write_file(data: dict[str, Any]): + env_path = resolve_address_config_path() + write_json_file(str(env_path), data) + + +def set_active(name: str): + data = read_address_config_file() + _guard_valid_address_name(data, name) + data["active"] = name + write_file(data) + + +def _guard_valid_address_name(env: Any, name: str): + envs = env.get("addresses", {}) + if name not in envs: + raise UnknownAddressAliasError(name) + + +def create_new_address_config(name: str, template: str): + data = read_address_config_file() + _guard_alias_unique(data, name) + new_address = {} + if template: + _guard_valid_address_name(data, template) + new_address = data["addresses"][template] + + data["active"] = name + data.setdefault("addresses", {}) + data["addresses"][name] = new_address + write_file(data) + + +def _guard_alias_unique(env: Any, name: str): + envs = env.get("addresses", {}) + if name in envs: + raise AliasAlreadyExistsError(name) + + +def delete_config_value(name: str): + """Deletes a key-value pair of the active address config.""" + _guard_valid_alias_deletion(name) + data = read_address_config_file() + active_env = data.get("active", "default") + data.setdefault("addresses", {}) + data["addresses"].setdefault(active_env, {}) + del data["addresses"][active_env][name] + write_file(data) + + +def delete_alias(name: str): + _guard_valid_alias_deletion(name) + data = read_address_config_file() + data["addresses"].pop(name, None) + if data["active"] == name: + data["active"] = "default" + write_file(data) + + +def _guard_valid_alias_deletion(name: str): + if name == "default": + raise AliasProtectedError(name) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 3e21b209..9216b838 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -9,6 +9,7 @@ from multiversx_sdk import LibraryConfig from rich.logging import RichHandler +import multiversx_sdk_cli.cli_address import multiversx_sdk_cli.cli_config import multiversx_sdk_cli.cli_contracts import multiversx_sdk_cli.cli_data @@ -112,6 +113,7 @@ def setup_parser(args: list[str]): subparsers = parser.add_subparsers() commands: list[Any] = [] + commands.append(multiversx_sdk_cli.cli_address.setup_parser(subparsers)) commands.append(multiversx_sdk_cli.cli_contracts.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_transactions.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_validators.setup_parser(args, subparsers)) diff --git a/multiversx_sdk_cli/cli_address.py b/multiversx_sdk_cli/cli_address.py new file mode 100644 index 00000000..ed1f9e64 --- /dev/null +++ b/multiversx_sdk_cli/cli_address.py @@ -0,0 +1,150 @@ +import logging +import os +from typing import Any + +from multiversx_sdk_cli import cli_shared +from multiversx_sdk_cli.address import ( + create_new_address_config, + delete_alias, + delete_config_value, + get_active_address, + get_value, + read_address_config_file, + resolve_address_config_path, + set_active, + set_value, +) +from multiversx_sdk_cli.utils import dump_out_json +from multiversx_sdk_cli.ux import confirm_continuation + +logger = logging.getLogger("cli.address") + + +def setup_parser(subparsers: Any) -> Any: + parser = cli_shared.add_group_subparser(subparsers, "address", "Configure MultiversX CLI to use a default wallet.") + subparsers = parser.add_subparsers() + + sub = cli_shared.add_command_subparser( + subparsers, "address", "new", "Creates a new address config and sets it as the active address." + ) + _add_alias_arg(sub) + sub.add_argument( + "--template", + required=False, + help="an address config from which to create the new address", + ) + sub.set_defaults(func=new_address_config) + + sub = cli_shared.add_command_subparser(subparsers, "address", "list", "List available addresses") + sub.set_defaults(func=list_addresses) + + sub = cli_shared.add_command_subparser(subparsers, "address", "dump", "Dumps the active address.") + sub.set_defaults(func=dump) + + sub = cli_shared.add_command_subparser(subparsers, "address", "get", "Gets a config value from the active address.") + sub.add_argument("value", type=str, help="the value to get from the active address (e.g. path)") + sub.set_defaults(func=get_address_config_value) + + sub = cli_shared.add_command_subparser(subparsers, "address", "set", "Sets a config value for the active address.") + sub.add_argument("key", type=str, help="the key to set for the active address (e.g. index)") + sub.add_argument("value", type=str, help="the value to set for the specified key") + sub.set_defaults(func=set_address_config_value) + + sub = cli_shared.add_command_subparser( + subparsers, "address", "delete", "Deletes a config value from the active address." + ) + sub.add_argument("value", type=str, help="the value to delete for the active address") + sub.set_defaults(func=delete_address_config_value) + + sub = cli_shared.add_command_subparser(subparsers, "address", "switch", "Switch to a different address.") + _add_alias_arg(sub) + sub.set_defaults(func=switch_address) + + sub = cli_shared.add_command_subparser( + subparsers, + "address", + "remove", + "Deletes an address using the alias. No default address will be set. Use `address switch` to set a new address.", + ) + _add_alias_arg(sub) + sub.set_defaults(func=remove_address) + + sub = cli_shared.add_command_subparser( + subparsers, + "address", + "reset", + "Deletes the config file. No default address will be set.", + ) + sub.set_defaults(func=delete_address_config_file) + + parser.epilog = cli_shared.build_group_epilog(subparsers) + return subparsers + + +def _add_alias_arg(sub: Any): + sub.add_argument("alias", type=str, help="the alias of the wallet") + + +def new_address_config(args: Any): + create_new_address_config(name=args.alias, template=args.template) + dump_out_json(get_active_address()) + + +def list_addresses(args: Any): + _ensure_address_config_file_exists() + + data = read_address_config_file() + dump_out_json(data) + + +def dump(args: Any): + _ensure_address_config_file_exists() + dump_out_json(get_active_address()) + + +def get_address_config_value(args: Any): + _ensure_address_config_file_exists() + value = get_value(args.value) + print(value) + + +def set_address_config_value(args: Any): + _ensure_address_config_file_exists() + set_value(args.key, args.value) + + +def delete_address_config_value(args: Any): + _ensure_address_config_file_exists() + + delete_config_value(args.value) + dump_out_json(get_active_address()) + + +def switch_address(args: Any): + _ensure_address_config_file_exists() + + set_active(args.alias) + dump_out_json(get_active_address()) + + +def remove_address(args: Any): + _ensure_address_config_file_exists() + delete_alias(args.alias) + + +def delete_address_config_file(args: Any): + address_file = resolve_address_config_path() + if not address_file.is_file(): + logger.info("Address config file not found. Aborting...") + return + + confirm_continuation(f"The file `{str(address_file)}` will be deleted. Do you want to continue? (y/n)") + os.remove(address_file) + logger.info("Successfully deleted the address config file.") + + +def _ensure_address_config_file_exists(): + address_file = resolve_address_config_path() + if not address_file.is_file(): + logger.info("Address config file not found. Aborting...") + exit(1) diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 4e418501..883c4ed0 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -176,3 +176,23 @@ def __init__(self, name: str): class EnvironmentAlreadyExistsError(KnownError): def __init__(self, name: str): super().__init__(f"Environment entry already exists: {name}.") + + +class UnknownAddressAliasError(KnownError): + def __init__(self, name: str): + super().__init__(f"Alias is not known: {name}.") + + +class AliasAlreadyExistsError(KnownError): + def __init__(self, name: str): + super().__init__(f"Alias already exists: {name}.") + + +class AliasProtectedError(KnownError): + def __init__(self, name: str): + super().__init__(f"This environment name is protected: {name}.") + + +class InvalidAddressConfigValue(KnownError): + def __init__(self, message: str): + super().__init__(message) From ae4f0025cbb72eb7659ad4e2b323dbfcbc3275cf Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 2 Jun 2025 15:00:07 +0300 Subject: [PATCH 21/64] fixes after review --- multiversx_sdk_cli/cli_multisig.py | 526 +++-------------------------- 1 file changed, 51 insertions(+), 475 deletions(-) diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index 6f904bf5..377c1551 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -62,7 +62,6 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: f"Deploy a Multisig Smart Contract.{output_description}", ) _add_bytecode_arg(sub) - _add_abi_arg(sub) sub.add_argument( "--quorum", type=int, @@ -77,25 +76,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: help="the bech32 addresses of the board members", ) cli_shared.add_metadata_arg(sub) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=False, with_receiver_arg=False) sub.set_defaults(func=deploy) @@ -105,28 +86,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "deposit", f"Deposit native tokens (EGLD) or ESDT tokens into a Multisig Smart Contract.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - 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) cli_shared.add_token_transfers_args(sub) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=deposit) @@ -136,28 +97,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "discard-action", f"Discard a proposed action. Signatures must be removed first via `unsign`.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) _add_action_id_arg(sub) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=discard_action) @@ -167,8 +108,6 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "discard-batch", f"Discard all the actions for the specified IDs.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) sub.add_argument( "--action-ids", required=True, @@ -176,25 +115,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: type=int, help="the IDs of the actions to discard", ) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=discard_batch) @@ -204,33 +125,13 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "add-board-member", f"Propose adding a new board member.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) sub.add_argument( "--board-member", required=True, type=str, help="the bech32 address of the proposed board member", ) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=add_board_member) @@ -240,33 +141,13 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "add-proposer", f"Propose adding a new proposer.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) sub.add_argument( "--proposer", required=True, type=str, help="the bech32 address of the proposed proposer", ) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=add_proposer) @@ -276,33 +157,13 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "remove-user", f"Propose removing a user from the Multisig Smart Contract.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) sub.add_argument( "--user", required=True, type=str, help="the bech32 address of the proposed user to be removed", ) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=remove_user) @@ -312,33 +173,13 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "change-quorum", f"Propose changing the quorum of the Multisig Smart Contract.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) sub.add_argument( "--quorum", required=True, type=int, help="the size of the new quorum (number of signatures required to approve a proposal)", ) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=change_quorum) @@ -348,8 +189,6 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "transfer-and-execute", f"Propose transferring EGLD and optionally calling a smart contract.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) sub.add_argument( "--opt-gas-limit", type=int, @@ -358,25 +197,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") sub.add_argument("--function", type=str, help="the function to call") _add_arguments_arg(sub) - 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=True, with_data=False) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=True) sub.set_defaults(func=transfer_and_execute) @@ -386,8 +207,6 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "transfer-and-execute-esdt", f"Propose transferring ESDTs and optionally calling a smart contract.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) cli_shared.add_token_transfers_args(sub) sub.add_argument( "--opt-gas-limit", @@ -397,25 +216,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") sub.add_argument("--function", type=str, help="the function to call") _add_arguments_arg(sub) - 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=True, with_data=False) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=True) sub.set_defaults(func=transfer_and_execute_esdt) @@ -425,8 +226,6 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "async-call", f"Propose a transaction in which the contract will perform an async call.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) cli_shared.add_token_transfers_args(sub) sub.add_argument( "--opt-gas-limit", @@ -436,25 +235,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") sub.add_argument("--function", type=str, help="the function to call") _add_arguments_arg(sub) - 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=True, with_data=False) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=True) sub.set_defaults(func=async_call) @@ -464,32 +245,12 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "deploy-from-source", f"Propose a smart contract deploy from a previously deployed smart contract.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) sub.add_argument("--contract-to-copy", required=True, type=str, help="the bech32 address of the contract to copy") sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to copy") - _add_arguments_arg(sub) cli_shared.add_metadata_arg(sub) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_arguments_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=deploy_from_source) @@ -499,35 +260,14 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "upgrade-from-source", f"Propose a smart contract upgrade from a previously deployed smart contract.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - sub.add_argument( "--contract-to-upgrade", required=True, type=str, help="the bech32 address of the contract to upgrade" ) sub.add_argument("--contract-to-copy", required=True, type=str, help="the bech32 address of the contract to copy") sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to copy") - _add_arguments_arg(sub) cli_shared.add_metadata_arg(sub) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_arguments_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=upgrade_from_source) @@ -537,29 +277,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "sign-action", f"Sign a proposed action.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - _add_action_id_arg(sub) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=sign_action) @@ -569,29 +288,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "sign-batch", f"Sign a batch of actions.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - sub.add_argument("--batch", required=True, type=int, help="the id of the batch to sign") - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=sign_batch) @@ -601,29 +299,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "sign-and-perform", f"Sign a proposed action and perform it. Works only if quorum is reached.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - _add_action_id_arg(sub) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=sign_and_perform) @@ -633,29 +310,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "sign-batch-and-perform", f"Sign a batch of actions and perform them. Works only if quorum is reached.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - sub.add_argument("--batch", required=True, type=int, help="the id of the batch to sign") - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=sign_batch_and_perform) @@ -665,29 +321,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "unsign-action", f"Unsign a proposed action.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - _add_action_id_arg(sub) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=unsign_action) @@ -697,29 +332,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "unsign-batch", f"Unsign a batch of actions.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - - sub.add_argument("--batch", required=True, type=int, help="the id of the batch to sign") - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + sub.add_argument("--batch", required=True, type=int, help="the id of the batch to unsign") + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=unsign_batch) @@ -729,9 +343,6 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "unsign-for-outdated-members", f"Unsign an action for outdated board members.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - _add_action_id_arg(sub) sub.add_argument( "--outdated-members", @@ -739,25 +350,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: type=int, help="IDs of the outdated board members", ) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=unsign_for_outdated_board_members) @@ -767,29 +360,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "perform-action", f"Perform an action that has reached quorum.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - _add_action_id_arg(sub) - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=perform_action) @@ -799,29 +371,8 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "perform-batch", f"Perform a batch of actions that has reached quorum.{output_description}", ) - _add_contract_arg(sub) - _add_abi_arg(sub) - - sub.add_argument("--batch", required=True, type=int, help="the id of the batch to sign") - 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) - - sub.add_argument( - "--wait-result", - action="store_true", - default=False, - help="signal to wait for the transaction result - only valid if --send is set", - ) - sub.add_argument( - "--timeout", - default=100, - help="max num of seconds to wait for result - only valid if --wait-result is set", - ) - cli_shared.add_broadcast_args(sub) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) + sub.add_argument("--batch", required=True, type=int, help="the id of the batch to perform") + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) sub.set_defaults(func=perform_batch) @@ -1090,6 +641,31 @@ def _add_arguments_arg(sub: Any): ) +def _add_common_args(args: Any, sub: Any, with_contract_arg: bool = True, with_receiver_arg: bool = False): + if with_contract_arg: + _add_contract_arg(sub) + + _add_abi_arg(sub) + 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=with_receiver_arg, with_data=False) + cli_shared.add_broadcast_args(sub) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + sub.add_argument( + "--wait-result", + action="store_true", + default=False, + help="signal to wait for the transaction result - only valid if --send is set", + ) + sub.add_argument( + "--timeout", + default=100, + help="max num of seconds to wait for result - only valid if --wait-result is set", + ) + + def deploy(args: Any): logger.debug("multisig.deploy") From 841262d03d9e5aa72a52148ef592956a251dc704 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 2 Jun 2025 16:08:00 +0300 Subject: [PATCH 22/64] small refactoring --- CLI.md | 220 +++++++++++++-------------- multiversx_sdk_cli/cli_governance.py | 69 +++------ 2 files changed, 127 insertions(+), 162 deletions(-) diff --git a/CLI.md b/CLI.md index c494ad69..c9023834 100644 --- a/CLI.md +++ b/CLI.md @@ -3175,7 +3175,6 @@ Output example: options: -h, --help show this help message and exit --bytecode BYTECODE the file containing the WASM bytecode - --abi ABI the ABI file of the Multisig Smart Contract --quorum QUORUM the number of signatures required to approve a proposal --board-members BOARD_MEMBERS [BOARD_MEMBERS ...] the bech32 addresses of the board members @@ -3183,6 +3182,7 @@ options: --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) --metadata-payable ‼ mark the contract as payable (default: not payable) --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) + --abi ABI the ABI file of the Multisig Smart Contract --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -3206,10 +3206,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is - set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -3228,6 +3224,10 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is + set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.Deposit @@ -3269,6 +3269,9 @@ Output example: options: -h, --help show this help message and exit + --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 --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract --outfile OUTFILE where to save the output (default: stdout) @@ -3294,13 +3297,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --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 - --wait-result signal to wait for the transaction result - only valid if --send is - set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -3319,6 +3315,10 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is + set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.DiscardAction @@ -3360,9 +3360,9 @@ Output example: options: -h, --help show this help message and exit + --action ACTION the id of the action --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --action ACTION the id of the action --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -3386,9 +3386,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -3407,6 +3404,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.DiscardBatch @@ -3448,9 +3448,9 @@ Output example: options: -h, --help show this help message and exit + --action-ids ACTION_IDS [ACTION_IDS ...] the IDs of the actions to discard --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --action-ids ACTION_IDS [ACTION_IDS ...] the IDs of the actions to discard --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -3474,9 +3474,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -3495,6 +3492,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.AddBoardMember @@ -3536,9 +3536,9 @@ Output example: options: -h, --help show this help message and exit + --board-member BOARD_MEMBER the bech32 address of the proposed board member --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --board-member BOARD_MEMBER the bech32 address of the proposed board member --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -3562,9 +3562,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -3583,6 +3580,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.AddProposer @@ -3624,9 +3624,9 @@ Output example: options: -h, --help show this help message and exit + --proposer PROPOSER the bech32 address of the proposed proposer --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --proposer PROPOSER the bech32 address of the proposed proposer --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -3650,9 +3650,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -3671,6 +3668,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.RemoveUser @@ -3712,9 +3712,9 @@ Output example: options: -h, --help show this help message and exit + --user USER the bech32 address of the proposed user to be removed --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --user USER the bech32 address of the proposed user to be removed --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -3738,9 +3738,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -3759,6 +3756,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.ChangeQuorum @@ -3800,10 +3800,10 @@ Output example: options: -h, --help show this help message and exit - --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract - --abi ABI the ABI file of the Multisig Smart Contract --quorum QUORUM the size of the new quorum (number of signatures required to approve a proposal) + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -3827,9 +3827,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -3848,6 +3845,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.TransferAndExecute @@ -3889,8 +3889,6 @@ Output example: options: -h, --help show this help message and exit - --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract - --abi ABI the ABI file of the Multisig Smart Contract --opt-gas-limit OPT_GAS_LIMIT the size of the new quorum (number of signatures required to approve a proposal) --contract-abi CONTRACT_ABI the ABI file of the contract to call @@ -3900,6 +3898,8 @@ options: 0xabba str:TOK-a1c2ef true addr:erd1[..] --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -3925,9 +3925,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -3946,6 +3943,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.TransferAndExecuteEsdt @@ -3987,8 +3987,6 @@ Output example: options: -h, --help show this help message and exit - --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract - --abi ABI the ABI file of the Multisig Smart Contract --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 @@ -4001,6 +3999,8 @@ options: 0xabba str:TOK-a1c2ef true addr:erd1[..] --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4026,10 +4026,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is - set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4048,6 +4044,10 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is + set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.AsyncCall @@ -4089,8 +4089,6 @@ Output example: options: -h, --help show this help message and exit - --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract - --abi ABI the ABI file of the Multisig Smart Contract --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 @@ -4103,6 +4101,8 @@ options: 0xabba str:TOK-a1c2ef true addr:erd1[..] --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4128,10 +4128,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is - set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4150,6 +4146,10 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is + set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.DeployFromSource @@ -4191,19 +4191,19 @@ Output example: options: -h, --help show this help message and exit - --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract - --abi ABI the ABI file of the Multisig Smart Contract --contract-to-copy CONTRACT_TO_COPY the bech32 address of the contract to copy --contract-abi CONTRACT_ABI the ABI file of the contract to copy + --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) + --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) + --metadata-payable ‼ mark the contract as payable (default: not payable) + --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 0xabba str:TOK-a1c2ef true addr:erd1[..] --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] - --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) - --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) - --metadata-payable ‼ mark the contract as payable (default: not payable) - --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4227,9 +4227,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4248,6 +4245,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.UpgradeFromSource @@ -4289,20 +4289,20 @@ Output example: options: -h, --help show this help message and exit - --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract - --abi ABI the ABI file of the Multisig Smart Contract --contract-to-upgrade CONTRACT_TO_UPGRADE the bech32 address of the contract to upgrade --contract-to-copy CONTRACT_TO_COPY the bech32 address of the contract to copy --contract-abi CONTRACT_ABI the ABI file of the contract to copy + --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) + --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) + --metadata-payable ‼ mark the contract as payable (default: not payable) + --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 0xabba str:TOK-a1c2ef true addr:erd1[..] --arguments-file ARGUMENTS_FILE a json file containing the arguments. ONLY if abi file is provided. E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }] - --metadata-not-upgradeable ‼ mark the contract as NOT upgradeable (default: upgradeable) - --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) - --metadata-payable ‼ mark the contract as payable (default: not payable) - --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4326,9 +4326,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4347,6 +4344,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.SignAction @@ -4388,9 +4388,9 @@ Output example: options: -h, --help show this help message and exit + --action ACTION the id of the action --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --action ACTION the id of the action --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4414,9 +4414,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4435,6 +4432,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.SignBatch @@ -4476,9 +4476,9 @@ Output example: options: -h, --help show this help message and exit + --batch BATCH the id of the batch to sign --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --batch BATCH the id of the batch to sign --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4502,9 +4502,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4523,6 +4520,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.SignAndPerform @@ -4564,9 +4564,9 @@ Output example: options: -h, --help show this help message and exit + --action ACTION the id of the action --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --action ACTION the id of the action --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4590,9 +4590,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4611,6 +4608,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.SignBatchAndPerform @@ -4652,9 +4652,9 @@ Output example: options: -h, --help show this help message and exit + --batch BATCH the id of the batch to sign --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --batch BATCH the id of the batch to sign --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4678,9 +4678,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4699,6 +4696,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.UnsignAction @@ -4740,9 +4740,9 @@ Output example: options: -h, --help show this help message and exit + --action ACTION the id of the action --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --action ACTION the id of the action --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4766,9 +4766,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4787,6 +4784,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.UnsignBatch @@ -4828,9 +4828,9 @@ Output example: options: -h, --help show this help message and exit + --batch BATCH the id of the batch to unsign --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --batch BATCH the id of the batch to sign --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4854,9 +4854,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4875,6 +4872,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.UnsignForOutdatedMembers @@ -4916,11 +4916,11 @@ Output example: options: -h, --help show this help message and exit - --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract - --abi ABI the ABI file of the Multisig Smart Contract --action ACTION the id of the action --outdated-members OUTDATED_MEMBERS [OUTDATED_MEMBERS ...] IDs of the outdated board members + --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract + --abi ABI the ABI file of the Multisig Smart Contract --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -4944,10 +4944,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is - set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -4966,6 +4962,10 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is + set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.PerformAction @@ -5007,9 +5007,9 @@ Output example: options: -h, --help show this help message and exit + --action ACTION the id of the action --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --action ACTION the id of the action --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -5033,9 +5033,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -5054,6 +5051,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.PerformBatch @@ -5095,9 +5095,9 @@ Output example: options: -h, --help show this help message and exit + --batch BATCH the id of the batch to perform --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract --abi ABI the ABI file of the Multisig Smart Contract - --batch BATCH the id of the batch to sign --outfile OUTFILE where to save the output (default: stdout) --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided @@ -5121,9 +5121,6 @@ options: --options OPTIONS the transaction options (default: 0) --relayer RELAYER the bech32 address of the relayer --guardian GUARDIAN the bech32 address of the guardian - --wait-result signal to wait for the transaction result - only valid if --send is set - --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is - set --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service @@ -5142,6 +5139,9 @@ options: --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) + --wait-result signal to wait for the transaction result - only valid if --send is set + --timeout TIMEOUT max num of seconds to wait for result - only valid if --wait-result is + set ``` ### Multisig.GetQuorum diff --git a/multiversx_sdk_cli/cli_governance.py b/multiversx_sdk_cli/cli_governance.py index 4105586c..3d04a7db 100644 --- a/multiversx_sdk_cli/cli_governance.py +++ b/multiversx_sdk_cli/cli_governance.py @@ -38,15 +38,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: _add_commit_hash_arg(sub) sub.add_argument("--start-vote-epoch", required=True, type=int, help="the epoch in which the voting will start") sub.add_argument("--end-vote-epoch", required=True, type=int, help="the epoch in which the voting will stop") - - 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) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) - cli_shared.add_outfile_arg(sub) - cli_shared.add_broadcast_args(sub) - cli_shared.add_wait_result_and_timeout_args(sub) + _add_common_args(args, sub) sub.set_defaults(func=create_proposal) @@ -65,15 +57,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: choices=["yes", "no", "veto", "abstain"], help="the type of vote", ) - - 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) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) - cli_shared.add_outfile_arg(sub) - cli_shared.add_broadcast_args(sub) - cli_shared.add_wait_result_and_timeout_args(sub) + _add_common_args(args, sub) sub.set_defaults(func=vote) @@ -85,14 +69,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: ) _add_proposal_nonce_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) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) - cli_shared.add_outfile_arg(sub) - cli_shared.add_broadcast_args(sub) - cli_shared.add_wait_result_and_timeout_args(sub) + _add_common_args(args, sub) sub.set_defaults(func=close_proposal) @@ -110,14 +87,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: type=str, help="a list of users who initiated the proposals (e.g. --proposers erd1..., erd1...)", ) - 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) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) - cli_shared.add_outfile_arg(sub) - cli_shared.add_broadcast_args(sub) - cli_shared.add_wait_result_and_timeout_args(sub) + _add_common_args(args, sub) sub.set_defaults(func=clear_ended_proposals) @@ -127,15 +97,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: "claim-accumulated-fees", f"Claim the accumulated fees.{CLIOutputBuilder.describe()}", ) - - 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) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) - cli_shared.add_outfile_arg(sub) - cli_shared.add_broadcast_args(sub) - cli_shared.add_wait_result_and_timeout_args(sub) + _add_common_args(args, sub) sub.set_defaults(func=claim_accumulated_fees) @@ -158,15 +120,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: ) sub.add_argument("--min-veto-threshold", required=True, type=int, help="the min veto threshold") sub.add_argument("--min-pass-threshold", required=True, type=int, help="the min pass threshold") - - 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) - cli_shared.add_guardian_wallet_args(args, sub) - cli_shared.add_relayed_v3_wallet_args(args, sub) - cli_shared.add_outfile_arg(sub) - cli_shared.add_broadcast_args(sub) - cli_shared.add_wait_result_and_timeout_args(sub) + _add_common_args(args, sub) sub.set_defaults(func=change_config) @@ -227,6 +181,17 @@ def _add_proposal_nonce_arg(sub: Any): sub.add_argument("--proposal-nonce", required=True, type=int, help="the nonce of the proposal") +def _add_common_args(args: Any, sub: Any): + 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) + cli_shared.add_guardian_wallet_args(args, sub) + cli_shared.add_relayed_v3_wallet_args(args, sub) + cli_shared.add_outfile_arg(sub) + cli_shared.add_broadcast_args(sub) + cli_shared.add_wait_result_and_timeout_args(sub) + + def create_proposal(args: Any): ensure_wallet_args_are_provided(args) validate_broadcast_args(args) From a0ca485c6005ddb858436720d6597cad71d22969 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 3 Jun 2025 15:51:39 +0300 Subject: [PATCH 23/64] use sender from config file --- multiversx_sdk_cli/args_validation.py | 10 - multiversx_sdk_cli/cli_contracts.py | 4 - multiversx_sdk_cli/cli_delegation.py | 2 - multiversx_sdk_cli/cli_shared.py | 70 +++++- multiversx_sdk_cli/cli_transactions.py | 2 - multiversx_sdk_cli/cli_validators.py | 2 - multiversx_sdk_cli/cli_wallet.py | 15 +- multiversx_sdk_cli/dns.py | 2 - multiversx_sdk_cli/errors.py | 5 + .../tests/test_cli_default_wallet.py | 207 ++++++++++++++++++ 10 files changed, 293 insertions(+), 26 deletions(-) create mode 100644 multiversx_sdk_cli/tests/test_cli_default_wallet.py diff --git a/multiversx_sdk_cli/args_validation.py b/multiversx_sdk_cli/args_validation.py index c15b290b..e6f79723 100644 --- a/multiversx_sdk_cli/args_validation.py +++ b/multiversx_sdk_cli/args_validation.py @@ -29,16 +29,6 @@ def validate_gas_limit_args(args: Any): raise InvalidArgumentsError("--gas-limit must be provided") -def ensure_wallet_args_are_provided(args: Any): - signing_methods = [args.pem, args.keyfile, args.ledger] - - if all(signing_methods): - raise InvalidArgumentsError("Only one of --pem, --keyfile, or --ledger must be provided") - - if not any(signing_methods): - raise InvalidArgumentsError("One of --pem, --keyfile, or --ledger must be provided") - - def ensure_relayer_wallet_args_are_provided(args: Any): signing_methods = [args.relayer_pem, args.relayer_keyfile, args.relayer_ledger] diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 38c5cb70..518b3691 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -18,7 +18,6 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.args_validation import ( - ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, validate_proxy_argument, @@ -381,7 +380,6 @@ def deploy(args: Any): cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_transaction_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) @@ -433,7 +431,6 @@ def call(args: Any): cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_transaction_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) @@ -478,7 +475,6 @@ def upgrade(args: Any): cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) validate_transaction_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index bba9ae6b..ea456c31 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -12,7 +12,6 @@ from multiversx_sdk_cli import cli_shared, errors, utils from multiversx_sdk_cli.args_validation import ( - ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, validate_nonce_args, @@ -390,7 +389,6 @@ def _add_common_arguments(args: list[str], sub: Any): def validate_arguments(args: Any): validate_nonce_args(args) validate_receiver_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index d09bf308..bc94f47b 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -17,7 +17,12 @@ Transaction, ) -from multiversx_sdk_cli import config, errors, utils +from multiversx_sdk_cli import config, utils +from multiversx_sdk_cli.address import ( + get_active_address, + read_address_config_file, + resolve_address_config_path, +) from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.cli_password import ( load_guardian_password, @@ -31,9 +36,13 @@ ) from multiversx_sdk_cli.env import MxpyEnv, get_address_hrp from multiversx_sdk_cli.errors import ( + AddressConfigFileError, ArgumentsNotProvidedError, BadUsage, IncorrectWalletError, + InvalidAddressConfigValue, + NoWalletProvided, + UnknownAddressAliasError, ) from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount @@ -155,6 +164,7 @@ def add_tx_args( def add_wallet_args(args: list[str], sub: Any): + sub.add_argument("--sender", type=str, help="the alias of the wallet set in the address config") sub.add_argument( "--pem", required=False, @@ -309,8 +319,64 @@ def prepare_account(args: Any): ) elif args.ledger: return LedgerAccount(address_index=args.sender_wallet_index) + elif args.sender: + file_path = resolve_address_config_path() + if not file_path.is_file(): + raise AddressConfigFileError("The address config file was not found") + + file = read_address_config_file() + if file == dict(): + raise AddressConfigFileError("Address config file is empty") + + addresses: dict[str, Any] = file["addresses"] + wallet = addresses.get(args.sender, None) + if not wallet: + raise UnknownAddressAliasError(args.sender) + + logger.info(f"Using sender [{args.sender}] from address config.") + return _load_wallet_from_address_config(wallet=wallet, hrp=hrp) + else: + active_address = get_active_address() + if active_address == dict(): + logger.info("No default wallet found in address config.") + raise NoWalletProvided() + + alias_of_default_wallet = read_address_config_file().get("active", "") + logger.info(f"Using sender [{alias_of_default_wallet}] from address config.") + + return _load_wallet_from_address_config(wallet=active_address, hrp=hrp) + + +def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Account: + kind = wallet.get("kind", None) + if not kind: + raise AddressConfigFileError("'kind' field must be set in the address config") + + if kind not in ["pem", "keystore"]: + raise InvalidAddressConfigValue("'kind' must be 'pem' or 'keystore'") + + path = wallet.get("path", None) + if not path: + raise AddressConfigFileError("'path' field must be set in the address config") + path = Path(path) + + index = int(wallet.get("index", 0)) + + if kind == "pem": + return Account.new_from_pem(file_path=path, index=index, hrp=hrp) else: - raise errors.NoWalletProvided() + password = wallet.get("password", "") + password_path = wallet.get("passwordPath", None) + + if not password and not password_path: + raise AddressConfigFileError( + "'password' or 'passwordPath' must be set in the address config for keystore wallets" + ) + + if password_path: + password = Path(password_path).read_text().splitlines()[0].strip() + + return Account.new_from_keystore(file_path=path, password=password, address_index=index, hrp=hrp) def _get_address_hrp(args: Any) -> str: diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 14dc8222..55b61487 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -14,7 +14,6 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.args_validation import ( ensure_relayer_wallet_args_are_provided, - ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, validate_nonce_args, @@ -120,7 +119,6 @@ def create_transaction(args: Any): validate_nonce_args(args) validate_receiver_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 9d6ebeba..019f4f94 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -5,7 +5,6 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.args_validation import ( - ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, validate_nonce_args, @@ -149,7 +148,6 @@ def _add_nodes_arg(sub: Any): def validate_args(args: Any) -> None: validate_nonce_args(args) validate_receiver_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 48d99048..44bf02ad 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -180,11 +180,13 @@ def wallet_new(args: Any): if format == WALLET_FORMAT_RAW_MNEMONIC: outfile.write_text(mnemonic.get_text()) elif format == WALLET_FORMAT_KEYSTORE_MNEMONIC: - password = getpass.getpass("Enter a new password (for keystore):") + password = _request_password() + wallet = UserWallet.from_mnemonic(mnemonic.get_text(), password) wallet.save(outfile) elif format == WALLET_FORMAT_KEYSTORE_SECRET_KEY: - password = getpass.getpass("Enter a new password (for keystore):") + password = _request_password() + secret_key = mnemonic.derive_key() wallet = UserWallet.from_secret_key(secret_key, password) wallet.save(outfile, address_hrp) @@ -200,6 +202,15 @@ def wallet_new(args: Any): logger.info(f"Wallet ({format}) saved: {outfile}") +def _request_password() -> str: + password = "" + while not len(password): + password = getpass.getpass("Enter a new password (for keystore):") + if not len(password): + logger.warning("No password provided") + return password + + def _generate_mnemonic_with_shard_constraint(shard: int) -> Mnemonic: if shard not in CURRENT_SHARDS: raise BadUserInput(f"Wrong shard provided. Choose between {CURRENT_SHARDS}") diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 2714ca5a..8cda4db5 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -10,7 +10,6 @@ from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.args_validation import ( - ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, validate_transaction_args, @@ -62,7 +61,6 @@ def validate_name(name: str, shard_id: int, proxy: INetworkProvider): def register(args: Any): validate_transaction_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 883c4ed0..0d64255a 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -196,3 +196,8 @@ def __init__(self, name: str): class InvalidAddressConfigValue(KnownError): def __init__(self, message: str): super().__init__(message) + + +class AddressConfigFileError(KnownError): + def __init__(self, message: str): + super().__init__(message) diff --git a/multiversx_sdk_cli/tests/test_cli_default_wallet.py b/multiversx_sdk_cli/tests/test_cli_default_wallet.py new file mode 100644 index 00000000..d3f98425 --- /dev/null +++ b/multiversx_sdk_cli/tests/test_cli_default_wallet.py @@ -0,0 +1,207 @@ +import json +from pathlib import Path +from typing import Any + +from multiversx_sdk_cli.cli import main + + +def test_empty_address_config(monkeypatch: Any, tmp_path: Path): + test_file = tmp_path / "addresses.mxpy.json" + test_file.write_text("{}") + + import multiversx_sdk_cli.address + + monkeypatch.setattr(multiversx_sdk_cli.address, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + multiversx_sdk_cli.address.read_address_config_file.cache_clear() + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + ] + ) + assert return_code + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--sender", + "invalidSender", + ] + ) + assert return_code + + +def test_without_address_config(monkeypatch: Any, tmp_path: Path): + test_file = tmp_path / "addresses.mxpy.json" + assert not test_file.exists() + + import multiversx_sdk_cli.address + + monkeypatch.setattr(multiversx_sdk_cli.address, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + multiversx_sdk_cli.address.read_address_config_file.cache_clear() + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + ] + ) + assert return_code + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--sender", + "invalidAlias", + ] + ) + assert return_code + + +def test_incomplete_address_config(monkeypatch: Any, tmp_path: Path): + test_file = tmp_path / "addresses.mxpy.json" + json_file = { + "active": "alice", + "addresses": { + "alice": { + "path": "/example/to/wallet.pem", + "index": "0", + }, + }, + } + test_file.write_text(json.dumps(json_file)) + + import multiversx_sdk_cli.address + + monkeypatch.setattr(multiversx_sdk_cli.address, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + multiversx_sdk_cli.address.read_address_config_file.cache_clear() + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + ] + ) + assert return_code + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--sender", + "alice", + ] + ) + assert return_code + + json_file = { + "active": "alice", + "addresses": { + "alice": { + "kind": "pem", + "index": "0", + }, + }, + } + test_file.write_text(json.dumps(json_file)) + + import multiversx_sdk_cli.address + + monkeypatch.setattr(multiversx_sdk_cli.address, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + multiversx_sdk_cli.address.read_address_config_file.cache_clear() + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + ] + ) + assert return_code + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--sender", + "alice", + ] + ) + assert return_code + + json_file = { + "active": "alice", + "addresses": { + "alice": { + "kind": "keystore", + "path": "/example/to/wallet.json", + "index": "0", + }, + }, + } + test_file.write_text(json.dumps(json_file)) + + import multiversx_sdk_cli.address + + monkeypatch.setattr(multiversx_sdk_cli.address, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + multiversx_sdk_cli.address.read_address_config_file.cache_clear() + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + ] + ) + assert return_code + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--sender", + "alice", + ] + ) + assert return_code From c8ed0f1f450b72a4ca20e27ddbf3b0f7ba190fbf Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 3 Jun 2025 16:52:38 +0300 Subject: [PATCH 24/64] fix after review --- CLI.md | 680 ++--------------------------- multiversx_sdk_cli/cli_multisig.py | 4 +- 2 files changed, 41 insertions(+), 643 deletions(-) diff --git a/CLI.md b/CLI.md index c9023834..9e6cc049 100644 --- a/CLI.md +++ b/CLI.md @@ -3154,22 +3154,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -3249,22 +3234,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -3340,22 +3310,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -3428,22 +3383,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -3516,22 +3456,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -3604,22 +3529,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -3692,22 +3602,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -3780,22 +3675,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -3869,22 +3749,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -3967,22 +3832,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4069,22 +3919,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4171,22 +4006,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4269,22 +4089,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4368,22 +4173,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4456,22 +4246,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4544,22 +4319,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4632,22 +4392,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4720,22 +4465,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4808,22 +4538,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4896,22 +4611,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -4987,22 +4687,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5075,22 +4760,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5163,22 +4833,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5207,22 +4862,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5251,22 +4891,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5295,22 +4920,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5339,22 +4949,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5384,22 +4979,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5428,22 +5008,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5472,22 +5037,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5518,22 +5068,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5563,22 +5098,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5607,22 +5127,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5652,22 +5157,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5696,22 +5186,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5740,22 +5215,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5785,22 +5245,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5830,22 +5275,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5875,22 +5305,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: @@ -5920,22 +5335,7 @@ Output example: "...": "..." }, "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash", - "contractAddress": "the address of the contract", - "transactionOnNetwork": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "simulation": { - "execution": { - "...": "..." - }, - "cost": { - "...": "..." - } - } + "emittedTransactionHash": "the transaction hash" } options: diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index d6ae8248..915ceaaf 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -51,9 +51,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: ) subparsers = parser.add_subparsers() - output_description = CLIOutputBuilder.describe( - with_contract=True, with_transaction_on_network=True, with_simulation=True - ) + output_description = CLIOutputBuilder.describe() sub = cli_shared.add_command_subparser( subparsers, From bd02111bcdeb69852cbd7638cd97304a68922277 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 4 Jun 2025 16:17:06 +0300 Subject: [PATCH 25/64] add get commands --- CLI.md | 315 ++++++++++++++++++++++++++++++- CLI.md.sh | 18 ++ multiversx_sdk_cli/cli.py | 2 + multiversx_sdk_cli/cli_get.py | 222 ++++++++++++++++++++++ multiversx_sdk_cli/cli_shared.py | 6 +- 5 files changed, 559 insertions(+), 4 deletions(-) create mode 100644 multiversx_sdk_cli/cli_get.py diff --git a/CLI.md b/CLI.md index 2cf2143a..96deb703 100644 --- a/CLI.md +++ b/CLI.md @@ -23,7 +23,7 @@ See: COMMAND GROUPS: - {contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet,env} + {address,contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet,env,get} TOP-LEVEL OPTIONS: -h, --help show this help message and exit @@ -33,6 +33,7 @@ TOP-LEVEL OPTIONS: ---------------------- COMMAND GROUPS summary ---------------------- +address Configure MultiversX CLI to use a default wallet. contract Deploy, upgrade and interact with Smart Contracts tx Create and broadcast Transactions validator Stake, UnStake, UnBond, Unjail and other actions useful for Validators @@ -47,6 +48,7 @@ staking-provider Staking provider omnitool dns Operations related to the Domain Name Service faucet Get xEGLD on Devnet or Testnet env Configure MultiversX CLI to use specific environment values. +get Get info from the network. ``` ## Group **Contract** @@ -123,6 +125,7 @@ options: --metadata-payable ‼ mark the contract as payable (default: not payable) --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) --outfile OUTFILE where to save the output (default: stdout) + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -217,6 +220,7 @@ options: -h, --help show this help message and exit --abi ABI the ABI file of the Smart Contract --outfile OUTFILE where to save the output (default: stdout) + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -321,6 +325,7 @@ options: --metadata-not-readable ‼ mark the contract as NOT readable (default: readable) --metadata-payable ‼ mark the contract as payable (default: not payable) --metadata-payable-by-sc ‼ mark the contract as payable by SC (default: not payable by SC) + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -413,6 +418,7 @@ options: --verifier-url VERIFIER_URL the url of the service that validates the contract --docker-image DOCKER_IMAGE the docker image used for the build --contract-variant CONTRACT_VARIANT in case of a multicontract, specify the contract variant you want to verify + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -503,6 +509,7 @@ Output example: options: -h, --help show this help message and exit + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -610,6 +617,7 @@ Output example: options: -h, --help show this help message and exit + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -725,6 +733,7 @@ Stake value into the Network options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -782,6 +791,7 @@ Unstake value options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -837,6 +847,7 @@ Unjail a Validator Node options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -892,6 +903,7 @@ Unbond tokens for a bls key options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -947,6 +959,7 @@ Change the reward address options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1002,6 +1015,7 @@ Claim rewards options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1056,6 +1070,7 @@ Unstake-nodes will unstake nodes for provided bls keys options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1111,6 +1126,7 @@ This command will un-stake the given amount (if value is greater than the existi options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1166,6 +1182,7 @@ It will unBond nodes options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1221,6 +1238,7 @@ It will unBond tokens, if provided value is bigger that topUp value will unBond options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1276,6 +1294,7 @@ Deletes duplicated keys from registered data options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1330,6 +1349,7 @@ It will reStake UnStaked nodes options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1424,6 +1444,7 @@ Create a new delegation system smart contract, transferred value must be greater options: -h, --help show this help message and exit --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1497,6 +1518,7 @@ options: --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes --delegation-contract DELEGATION_CONTRACT bech32 address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1554,6 +1576,7 @@ options: --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1611,6 +1634,7 @@ options: --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes --delegation-contract DELEGATION_CONTRACT bech32 address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1668,6 +1692,7 @@ options: --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1725,6 +1750,7 @@ options: --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1782,6 +1808,7 @@ options: --validators-pem VALIDATORS_PEM a PEM file holding the BLS keys; can contain multiple nodes --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1837,6 +1864,7 @@ options: -h, --help show this help message and exit --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1892,6 +1920,7 @@ options: -h, --help show this help message and exit --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -1947,6 +1976,7 @@ options: -h, --help show this help message and exit --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -2002,6 +2032,7 @@ options: -h, --help show this help message and exit --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -2057,6 +2088,7 @@ options: -h, --help show this help message and exit --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -2113,6 +2145,7 @@ options: --service-fee SERVICE_FEE new service fee value --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -2169,6 +2202,7 @@ options: --delegation-cap DELEGATION_CAP new delegation contract capacity --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -2226,6 +2260,7 @@ options: --unset set automatic activation False --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -2283,6 +2318,7 @@ options: --unset set redelegate cap False --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -2341,6 +2377,7 @@ options: --identifier IDENTIFIER identifier field in staking provider metadata --delegation-contract DELEGATION_CONTRACT address of the delegation contract --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -2399,6 +2436,7 @@ options: --fee FEE service fee as hundredths of percents. (e.g. a service fee of 37.45 percent is expressed by the integer 3745) --proxy PROXY 🔗 the URL of the proxy + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -2541,6 +2579,7 @@ Sign a message options: -h, --help show this help message and exit --message MESSAGE the message you want to sign + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -3062,6 +3101,7 @@ Request xEGLD. options: -h, --help show this help message and exit + --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not @@ -3227,3 +3267,276 @@ options: -h, --help show this help message and exit ``` +## Group **Address** + + +``` +$ mxpy address --help +usage: mxpy address COMMAND [-h] ... + +Configure MultiversX CLI to use a default wallet. + +COMMANDS: + {new,list,dump,get,set,delete,switch,remove,reset} + +OPTIONS: + -h, --help show this help message and exit + +---------------- +COMMANDS summary +---------------- +new Creates a new address config and sets it as the active address. +list List available addresses +dump Dumps the active address. +get Gets a config value from the active address. +set Sets a config value for the active address. +delete Deletes a config value from the active address. +switch Switch to a different address. +remove Deletes an address using the alias. No default address will be set. Use `address switch` to set a new address. +reset Deletes the config file. No default address will be set. + +``` +### Address.New + + +``` +$ mxpy address new --help +usage: mxpy address new [-h] ... + +Creates a new address config and sets it as the active address. + +positional arguments: + alias the alias of the wallet + +options: + -h, --help show this help message and exit + --template TEMPLATE an address config from which to create the new address + +``` +### Address.List + + +``` +$ mxpy address list --help +usage: mxpy address list [-h] ... + +List available addresses + +options: + -h, --help show this help message and exit + +``` +### Address.Dump + + +``` +$ mxpy address dump --help +usage: mxpy address dump [-h] ... + +Dumps the active address. + +options: + -h, --help show this help message and exit + +``` +### Address.Get + + +``` +$ mxpy address get --help +usage: mxpy address get [-h] ... + +Gets a config value from the active address. + +positional arguments: + value the value to get from the active address (e.g. path) + +options: + -h, --help show this help message and exit + +``` +### Address.Set + + +``` +$ mxpy address set --help +usage: mxpy address set [-h] ... + +Sets a config value for the active address. + +positional arguments: + key the key to set for the active address (e.g. index) + value the value to set for the specified key + +options: + -h, --help show this help message and exit + +``` +### Address.Set + + +``` +$ mxpy address delete --help +usage: mxpy address delete [-h] ... + +Deletes a config value from the active address. + +positional arguments: + value the value to delete for the active address + +options: + -h, --help show this help message and exit + +``` +### Address.Switch + + +``` +$ mxpy address switch --help +usage: mxpy address switch [-h] ... + +Switch to a different address. + +positional arguments: + alias the alias of the wallet + +options: + -h, --help show this help message and exit + +``` +### Address.Remove + + +``` +$ mxpy address remove --help +usage: mxpy address remove [-h] ... + +Deletes an address using the alias. No default address will be set. Use `address switch` to set a new address. + +positional arguments: + alias the alias of the wallet + +options: + -h, --help show this help message and exit + +``` +### Address.Reset + + +``` +$ mxpy address reset --help +usage: mxpy address reset [-h] ... + +Deletes the config file. No default address will be set. + +options: + -h, --help show this help message and exit + +``` +## Group **Get** + + +``` +$ mxpy get --help +usage: mxpy get COMMAND [-h] ... + +Get info from the network. + +COMMANDS: + {account,keys,storage-entry,token,transaction} + +OPTIONS: + -h, --help show this help message and exit + +---------------- +COMMANDS summary +---------------- +account Get info about an account. +keys Get the storage (key-value pairs) of an account. +storage-entry Get a specific storage entry (key-value pair) of an account. +token Get a token of an account. +transaction Get a transaction from the network. + +``` +### Get.Account + + +``` +$ mxpy get account --help +usage: mxpy get account [-h] ... + +Get info about an account. + +options: + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet if configured in address config + --address ADDRESS the bech32 address + --proxy PROXY the proxy url + --balance whether to only fetch the balance of the address + +``` +### Get.Keys + + +``` +$ mxpy get keys --help +usage: mxpy get keys [-h] ... + +Get the storage (key-value pairs) of an account. + +options: + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet if configured in address config + --address ADDRESS the bech32 address + --proxy PROXY the proxy url + +``` +### Get.StorageEntry + + +``` +$ mxpy get storage-entry --help +usage: mxpy get storage-entry [-h] ... + +Get a specific storage entry (key-value pair) of an account. + +options: + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet if configured in address config + --address ADDRESS the bech32 address + --proxy PROXY the proxy url + --key KEY the storage key to read from + +``` +### Get.Token + + +``` +$ mxpy get token --help +usage: mxpy get token [-h] ... + +Get a token of an account. + +options: + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet if configured in address config + --address ADDRESS the bech32 address + --proxy PROXY the proxy url + --identifier IDENTIFIER the token identifier. Works for ESDT and NFT. (e.g. FNG-123456, NFT-987654-0a) + +``` +### Get.Transaction + + +``` +$ mxpy get transaction --help +usage: mxpy get transaction [-h] ... + +Get a transaction from the network. + +options: + -h, --help show this help message and exit + --proxy PROXY the proxy url + --hash HASH the transaction hash + +``` diff --git a/CLI.md.sh b/CLI.md.sh index e40cf91e..7d1538f3 100755 --- a/CLI.md.sh +++ b/CLI.md.sh @@ -140,6 +140,24 @@ generate() { command "Environment.List" "env list" command "Environment.Remove" "env remove" command "Environment.Reset" "env reset" + + group "Address" "address" + command "Address.New" "address new" + command "Address.List" "address list" + command "Address.Dump" "address dump" + command "Address.Get" "address get" + command "Address.Set" "address set" + command "Address.Set" "address delete" + command "Address.Switch" "address switch" + command "Address.Remove" "address remove" + command "Address.Reset" "address reset" + + group "Get" "get" + command "Get.Account" "get account" + command "Get.Keys" "get keys" + command "Get.StorageEntry" "get storage-entry" + command "Get.Token" "get token" + command "Get.Transaction" "get transaction" } generate diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 9216b838..e7509e8a 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -18,6 +18,7 @@ import multiversx_sdk_cli.cli_dns import multiversx_sdk_cli.cli_env import multiversx_sdk_cli.cli_faucet +import multiversx_sdk_cli.cli_get import multiversx_sdk_cli.cli_ledger import multiversx_sdk_cli.cli_localnet import multiversx_sdk_cli.cli_transactions @@ -128,6 +129,7 @@ def setup_parser(args: list[str]): commands.append(multiversx_sdk_cli.cli_dns.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_faucet.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_env.setup_parser(subparsers)) + commands.append(multiversx_sdk_cli.cli_get.setup_parser(subparsers)) parser.epilog = """ ---------------------- diff --git a/multiversx_sdk_cli/cli_get.py b/multiversx_sdk_cli/cli_get.py new file mode 100644 index 00000000..e59ebaee --- /dev/null +++ b/multiversx_sdk_cli/cli_get.py @@ -0,0 +1,222 @@ +import logging +from typing import Any, Optional + +from multiversx_sdk import Address, ProxyNetworkProvider, Token, TokenComputer + +from multiversx_sdk_cli import cli_shared +from multiversx_sdk_cli.address import ( + get_active_address, + read_address_config_file, + resolve_address_config_path, +) +from multiversx_sdk_cli.config import get_config_for_network_providers +from multiversx_sdk_cli.env import MxpyEnv +from multiversx_sdk_cli.errors import ( + AddressConfigFileError, + ArgumentsNotProvidedError, + BadUsage, + NoWalletProvided, + UnknownAddressAliasError, +) +from multiversx_sdk_cli.utils import dump_out_json + +logger = logging.getLogger("cli.get") + + +def setup_parser(subparsers: Any) -> Any: + parser = cli_shared.add_group_subparser(subparsers, "get", "Get info from the network.") + subparsers = parser.add_subparsers() + + sub = cli_shared.add_command_subparser(subparsers, "get", "account", "Get info about an account.") + _add_alias_arg(sub) + _add_address_arg(sub) + _add_proxy_arg(sub) + sub.add_argument( + "--balance", + action="store_true", + default=False, + required=False, + help="whether to only fetch the balance of the address", + ) + sub.set_defaults(func=get_account) + + sub = cli_shared.add_command_subparser( + subparsers, "get", "keys", "Get the storage (key-value pairs) of an account." + ) + _add_alias_arg(sub) + _add_address_arg(sub) + _add_proxy_arg(sub) + sub.set_defaults(func=get_keys) + + sub = cli_shared.add_command_subparser( + subparsers, "get", "storage-entry", "Get a specific storage entry (key-value pair) of an account." + ) + _add_alias_arg(sub) + _add_address_arg(sub) + _add_proxy_arg(sub) + sub.add_argument("--key", type=str, required=True, help="the storage key to read from") + sub.set_defaults(func=get_key) + + sub = cli_shared.add_command_subparser(subparsers, "get", "token", "Get a token of an account.") + _add_alias_arg(sub) + _add_address_arg(sub) + _add_proxy_arg(sub) + sub.add_argument( + "--identifier", + type=str, + required=True, + help="the token identifier. Works for ESDT and NFT. (e.g. FNG-123456, NFT-987654-0a)", + ) + sub.set_defaults(func=get_token) + + sub = cli_shared.add_command_subparser(subparsers, "get", "transaction", "Get a transaction from the network.") + _add_proxy_arg(sub) + sub.add_argument("--hash", type=str, required=True, help="the transaction hash") + sub.set_defaults(func=get_transaction) + + parser.epilog = cli_shared.build_group_epilog(subparsers) + return subparsers + + +def _add_alias_arg(sub: Any): + sub.add_argument("--alias", type=str, help="the alias of the wallet if configured in address config") + + +def _add_address_arg(sub: Any): + sub.add_argument("--address", type=str, help="the bech32 address") + + +def _add_proxy_arg(sub: Any): + sub.add_argument("--proxy", type=str, help="the proxy url") + + +def get_account(args: Any): + if args.alias and args.address: + raise BadUsage("Provide either '--alias' or '--address'") + + if args.address: + address = Address.new_from_bech32(args.address) + else: + hrp = cli_shared._get_address_hrp(args) + address = _get_address_from_alias_or_config(args.alias, hrp) + + proxy = _get_proxy(args) + + logger.info(f"Fetching details about {address.to_bech32()}") + response = proxy.get_account(address) + + if args.balance: + print(response.balance) + else: + dump_out_json(response.raw) + + +def get_keys(args: Any): + if args.alias and args.address: + raise BadUsage("Provide either '--alias' or '--address'") + + if args.address: + address = Address.new_from_bech32(args.address) + else: + hrp = cli_shared._get_address_hrp(args) + address = _get_address_from_alias_or_config(args.alias, hrp) + + proxy = _get_proxy(args) + + logger.info(f"Fetching details about {address.to_bech32()}") + response = proxy.get_account_storage(address) + + dump_out_json(response.raw) + + +def get_key(args: Any): + if args.alias and args.address: + raise BadUsage("Provide either '--alias' or '--address'") + + if args.address: + address = Address.new_from_bech32(args.address) + else: + hrp = cli_shared._get_address_hrp(args) + address = _get_address_from_alias_or_config(args.alias, hrp) + + proxy = _get_proxy(args) + + logger.info(f"Fetching details about {address.to_bech32()}") + response = proxy.get_account_storage_entry(address, args.key) + + dump_out_json(response.raw) + + +def get_token(args: Any): + if args.alias and args.address: + raise BadUsage("Provide either '--alias' or '--address'") + + if args.address: + address = Address.new_from_bech32(args.address) + else: + hrp = cli_shared._get_address_hrp(args) + address = _get_address_from_alias_or_config(args.alias, hrp) + + proxy = _get_proxy(args) + + logger.info(f"Fetching token from {address.to_bech32()}") + + token_computer = TokenComputer() + identifier = token_computer.extract_identifier_from_extended_identifier(args.identifier) + nonce = token_computer.extract_nonce_from_extended_identifier(args.identifier) + token = Token(identifier, nonce) + + response = proxy.get_token_of_account(address, token) + + dump_out_json(response.raw) + + +def get_transaction(args: Any): + proxy = _get_proxy(args) + response = proxy.get_transaction(args.hash) + + dump_out_json(response.raw) + + +def _get_address_from_alias_or_config(alias: Optional[str], hrp: str) -> Address: + if alias: + file_path = resolve_address_config_path() + if not file_path.is_file(): + raise AddressConfigFileError("The address config file was not found") + + file = read_address_config_file() + if file == dict(): + raise AddressConfigFileError("Address config file is empty") + + addresses: dict[str, Any] = file["addresses"] + wallet = addresses.get(alias, None) + if not wallet: + raise UnknownAddressAliasError(alias) + + logger.info(f"Using address of [{alias}] from address config.") + account = cli_shared.load_wallet_from_address_config(wallet=wallet, hrp=hrp) + return account.address + else: + active_address = get_active_address() + if active_address == dict(): + logger.info("No default wallet found in address config.") + raise NoWalletProvided() + + alias_of_default_wallet = read_address_config_file().get("active", "") + logger.info(f"Using address of [{alias_of_default_wallet}] from address config.") + + account = cli_shared.load_wallet_from_address_config(wallet=active_address, hrp=hrp) + return account.address + + +def _get_proxy(args: Any) -> ProxyNetworkProvider: + if not args.proxy: + env = MxpyEnv.from_active_env() + if env.proxy_url: + logger.info(f"Using proxy URL from config: {env.proxy_url}") + args.proxy = env.proxy_url + else: + raise ArgumentsNotProvidedError("'--proxy' was not provided") + + config = get_config_for_network_providers() + return ProxyNetworkProvider(url=args.proxy, config=config) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index bc94f47b..79dcf000 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -334,7 +334,7 @@ def prepare_account(args: Any): raise UnknownAddressAliasError(args.sender) logger.info(f"Using sender [{args.sender}] from address config.") - return _load_wallet_from_address_config(wallet=wallet, hrp=hrp) + return load_wallet_from_address_config(wallet=wallet, hrp=hrp) else: active_address = get_active_address() if active_address == dict(): @@ -344,10 +344,10 @@ def prepare_account(args: Any): alias_of_default_wallet = read_address_config_file().get("active", "") logger.info(f"Using sender [{alias_of_default_wallet}] from address config.") - return _load_wallet_from_address_config(wallet=active_address, hrp=hrp) + return load_wallet_from_address_config(wallet=active_address, hrp=hrp) -def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Account: +def load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Account: kind = wallet.get("kind", None) if not kind: raise AddressConfigFileError("'kind' field must be set in the address config") From 9cd7da8a5704df8dff37ec4015579acf90f0ce81 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 5 Jun 2025 11:54:14 +0300 Subject: [PATCH 26/64] create a method to set transaction fields --- .../base_transactions_controller.py | 17 ++++ multiversx_sdk_cli/governance.py | 90 ++++++++++--------- 2 files changed, 65 insertions(+), 42 deletions(-) diff --git a/multiversx_sdk_cli/base_transactions_controller.py b/multiversx_sdk_cli/base_transactions_controller.py index 49a6fb62..60f685db 100644 --- a/multiversx_sdk_cli/base_transactions_controller.py +++ b/multiversx_sdk_cli/base_transactions_controller.py @@ -23,6 +23,7 @@ ) from multiversx_sdk_cli.cosign_transaction import cosign_transaction from multiversx_sdk_cli.errors import BadUserInput +from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount logger = logging.getLogger("base_controller") @@ -144,3 +145,19 @@ def ensure_even_length(self, string: str) -> str: if len(string) % 2 == 1: return "0" + string return string + + def _set_transaction_fields( + self, + transaction: Transaction, + nonce: int, + version: int, + options: int, + gas_price: int, + guardian_and_relayer_data: GuardianRelayerData, + ): + transaction.nonce = nonce + transaction.version = version + transaction.options = options + transaction.gas_price = gas_price + transaction.guardian = guardian_and_relayer_data.guardian_address + transaction.relayer = guardian_and_relayer_data.relayer_address diff --git a/multiversx_sdk_cli/governance.py b/multiversx_sdk_cli/governance.py index 8d0e6e2b..8042d64d 100644 --- a/multiversx_sdk_cli/governance.py +++ b/multiversx_sdk_cli/governance.py @@ -36,15 +36,16 @@ def create_transaction_for_new_proposal( end_vote_epoch=end_vote_epoch, native_token_amount=native_token_amount, ) - tx.nonce = nonce - tx.version = version - tx.options = options - tx.gas_price = gas_price - tx.guardian = guardian_and_relayer_data.guardian_address - tx.relayer = guardian_and_relayer_data.relayer_address + self._set_transaction_fields( + transaction=tx, + nonce=nonce, + version=version, + options=options, + gas_price=gas_price, + guardian_and_relayer_data=guardian_and_relayer_data, + ) self.add_extra_gas_limit_if_required(tx) - if gas_limit: tx.gas_limit = gas_limit @@ -76,15 +77,16 @@ def create_transaction_for_voting( tx = self.factory.create_transaction_for_voting( sender=sender.address, proposal_nonce=proposal_nonce, vote=vote_value ) - tx.nonce = nonce - tx.version = version - tx.options = options - tx.gas_price = gas_price - tx.guardian = guardian_and_relayer_data.guardian_address - tx.relayer = guardian_and_relayer_data.relayer_address + self._set_transaction_fields( + transaction=tx, + nonce=nonce, + version=version, + options=options, + gas_price=gas_price, + guardian_and_relayer_data=guardian_and_relayer_data, + ) self.add_extra_gas_limit_if_required(tx) - if gas_limit: tx.gas_limit = gas_limit @@ -111,15 +113,16 @@ def create_transaction_for_closing_proposal( guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self.factory.create_transaction_for_closing_proposal(sender=sender.address, proposal_nonce=proposal_nonce) - tx.nonce = nonce - tx.version = version - tx.options = options - tx.gas_price = gas_price - tx.guardian = guardian_and_relayer_data.guardian_address - tx.relayer = guardian_and_relayer_data.relayer_address + self._set_transaction_fields( + transaction=tx, + nonce=nonce, + version=version, + options=options, + gas_price=gas_price, + guardian_and_relayer_data=guardian_and_relayer_data, + ) self.add_extra_gas_limit_if_required(tx) - if gas_limit: tx.gas_limit = gas_limit @@ -146,15 +149,16 @@ def create_transaction_for_clearing_ended_proposals( guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self.factory.create_transaction_for_clearing_ended_proposals(sender=sender.address, proposers=proposers) - tx.nonce = nonce - tx.version = version - tx.options = options - tx.gas_price = gas_price - tx.guardian = guardian_and_relayer_data.guardian_address - tx.relayer = guardian_and_relayer_data.relayer_address + self._set_transaction_fields( + transaction=tx, + nonce=nonce, + version=version, + options=options, + gas_price=gas_price, + guardian_and_relayer_data=guardian_and_relayer_data, + ) self.add_extra_gas_limit_if_required(tx) - if gas_limit: tx.gas_limit = gas_limit @@ -180,15 +184,16 @@ def create_transaction_for_claiming_accumulated_fees( guardian_and_relayer_data: GuardianRelayerData, ) -> Transaction: tx = self.factory.create_transaction_for_claiming_accumulated_fees(sender=sender.address) - tx.nonce = nonce - tx.version = version - tx.options = options - tx.gas_price = gas_price - tx.guardian = guardian_and_relayer_data.guardian_address - tx.relayer = guardian_and_relayer_data.relayer_address + self._set_transaction_fields( + transaction=tx, + nonce=nonce, + version=version, + options=options, + gas_price=gas_price, + guardian_and_relayer_data=guardian_and_relayer_data, + ) self.add_extra_gas_limit_if_required(tx) - if gas_limit: tx.gas_limit = gas_limit @@ -226,15 +231,16 @@ def create_transaction_for_changing_config( min_veto_threshold=min_veto_threshold, min_pass_threshold=min_pass_threshold, ) - tx.nonce = nonce - tx.version = version - tx.options = options - tx.gas_price = gas_price - tx.guardian = guardian_and_relayer_data.guardian_address - tx.relayer = guardian_and_relayer_data.relayer_address + self._set_transaction_fields( + transaction=tx, + nonce=nonce, + version=version, + options=options, + gas_price=gas_price, + guardian_and_relayer_data=guardian_and_relayer_data, + ) self.add_extra_gas_limit_if_required(tx) - if gas_limit: tx.gas_limit = gas_limit From 32787af5bb8c34cf5d32950bca175d5bed922331 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 5 Jun 2025 16:07:26 +0300 Subject: [PATCH 27/64] refactoring --- multiversx_sdk_cli/cli_contracts.py | 14 ++--- multiversx_sdk_cli/cli_delegation.py | 58 ++++++------------- multiversx_sdk_cli/cli_dns.py | 13 ++--- multiversx_sdk_cli/cli_governance.py | 4 +- multiversx_sdk_cli/cli_multisig.py | 7 +-- multiversx_sdk_cli/cli_shared.py | 13 +++-- multiversx_sdk_cli/cli_transactions.py | 15 ++--- multiversx_sdk_cli/cli_validators.py | 34 ++++------- .../tests/test_cli_governance.py | 3 +- multiversx_sdk_cli/tests/test_cli_multisig.py | 3 +- 10 files changed, 60 insertions(+), 104 deletions(-) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index f1155985..3cb6c705 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -319,8 +319,7 @@ def build(args: Any): def deploy(args: Any): logger.debug("deploy") - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_transaction_args(args) ensure_wallet_args_are_provided(args) @@ -363,6 +362,8 @@ def deploy(args: Any): contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=tx.nonce) logger.info("Contract address: %s", contract_address.to_bech32()) + + cli_config = MxpyEnv.from_active_env() utils.log_explorer_contract_address(args.chain, contract_address.to_bech32(), cli_config.explorer_url) _send_or_simulate(tx, contract_address, args) @@ -371,8 +372,7 @@ def deploy(args: Any): def call(args: Any): logger.debug("call") - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_transaction_args(args) ensure_wallet_args_are_provided(args) @@ -420,8 +420,7 @@ def call(args: Any): def upgrade(args: Any): logger.debug("upgrade") - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_transaction_args(args) ensure_wallet_args_are_provided(args) @@ -468,8 +467,7 @@ def upgrade(args: Any): def query(args: Any): logger.debug("query") - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_proxy_argument(args) diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index bba9ae6b..73d61a0e 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -21,7 +21,6 @@ ) from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.delegation import DelegationOperations -from multiversx_sdk_cli.env import MxpyEnv def setup_parser(args: list[str], subparsers: Any) -> Any: @@ -403,8 +402,7 @@ def _get_delegation_controller(args: Any): def do_create_delegation_contract(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -434,8 +432,7 @@ def do_create_delegation_contract(args: Any): def get_contract_address_by_deploy_tx_hash(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_proxy_argument(args) @@ -454,8 +451,7 @@ def get_contract_address_by_deploy_tx_hash(args: Any): def add_new_nodes(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -505,8 +501,7 @@ def _get_public_keys_and_signed_messages(args: Any) -> tuple[list[ValidatorPubli def remove_nodes(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) @@ -557,8 +552,7 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def stake_nodes(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -598,8 +592,7 @@ def _check_if_either_bls_keys_or_validators_file_are_provided(args: Any): def unbond_nodes(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -631,8 +624,7 @@ def unbond_nodes(args: Any): def unstake_nodes(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -664,8 +656,7 @@ def unstake_nodes(args: Any): def unjail_nodes(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -697,8 +688,7 @@ def unjail_nodes(args: Any): def delegate(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -728,8 +718,7 @@ def delegate(args: Any): def claim_rewards(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -757,8 +746,7 @@ def claim_rewards(args: Any): def redelegate_rewards(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -786,8 +774,7 @@ def redelegate_rewards(args: Any): def undelegate(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -817,8 +804,7 @@ def undelegate(args: Any): def withdraw(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -847,8 +833,7 @@ def withdraw(args: Any): def change_service_fee(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -878,8 +863,7 @@ def change_service_fee(args: Any): def modify_delegation_cap(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -909,8 +893,7 @@ def modify_delegation_cap(args: Any): def automatic_activation(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -941,8 +924,7 @@ def automatic_activation(args: Any): def redelegate_cap(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -973,8 +955,7 @@ def redelegate_cap(args: Any): def set_metadata(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) @@ -1006,8 +987,7 @@ def set_metadata(args: Any): def make_new_contract_from_validator_data(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_arguments(args) diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index 010450bc..734dde82 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -17,7 +17,6 @@ validate_name, version, ) -from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.errors import ArgumentsNotProvidedError @@ -137,8 +136,7 @@ def _ensure_proxy_is_provided(args: Any): def dns_resolve(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) _ensure_proxy_is_provided(args) @@ -149,8 +147,7 @@ def dns_resolve(args: Any): def dns_validate_name(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) _ensure_proxy_is_provided(args) @@ -175,8 +172,7 @@ def get_dns_address_for_name_hex(args: Any): def get_registration_cost(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) _ensure_proxy_is_provided(args) @@ -185,8 +181,7 @@ def get_registration_cost(args: Any): def get_version(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) _ensure_proxy_is_provided(args) diff --git a/multiversx_sdk_cli/cli_governance.py b/multiversx_sdk_cli/cli_governance.py index 3458a946..337f614c 100644 --- a/multiversx_sdk_cli/cli_governance.py +++ b/multiversx_sdk_cli/cli_governance.py @@ -19,7 +19,6 @@ ) from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers -from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.governance import GovernanceWrapper @@ -200,8 +199,7 @@ def _ensure_args(args: Any): def _ensure_proxy_arg(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_proxy_argument(args) diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index 3a9c92d8..e9ba68ae 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -38,7 +38,6 @@ from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS -from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.multisig import MultisigWrapper logger = logging.getLogger("cli.multisig") @@ -656,8 +655,7 @@ def _add_common_args(args: Any, sub: Any, with_contract_arg: bool = True, with_r def _ensure_args(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_transaction_args(args) ensure_wallet_args_are_provided(args) @@ -666,8 +664,7 @@ def _ensure_args(args: Any): def _ensure_proxy_arg(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_proxy_argument(args) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index ec6bfbb1..9cc06559 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -603,16 +603,15 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL output_builder.set_emitted_transaction(tx) outfile = args.outfile if hasattr(args, "outfile") else None - cli_config = MxpyEnv.from_active_env() hash = b"" try: if send_wait_result: - _confirm_continuation_if_required(cli_config, tx) + _confirm_continuation_if_required(tx) transaction_on_network = send_and_wait_for_result(tx, proxy, args.timeout) output_builder.set_awaited_transaction(transaction_on_network) elif send_only: - _confirm_continuation_if_required(cli_config, tx) + _confirm_continuation_if_required(tx) hash = proxy.send_transaction(tx) output_builder.set_emitted_transaction_hash(hash.hex()) @@ -626,6 +625,7 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL utils.dump_out_json(output_transaction, outfile=outfile) if send_only and hash: + cli_config = MxpyEnv.from_active_env() log_explorer_transaction( chain=output_transaction["emittedTransaction"]["chainID"], transaction_hash=output_transaction["emittedTransactionHash"], @@ -635,7 +635,9 @@ def send_or_simulate(tx: Transaction, args: Any, dump_output: bool = True) -> CL return output_builder -def _confirm_continuation_if_required(env: MxpyEnv, tx: Transaction) -> None: +def _confirm_continuation_if_required(tx: Transaction) -> None: + env = MxpyEnv.from_active_env() + if env.ask_confirmation: transaction = tx.to_dictionary() @@ -702,12 +704,13 @@ def prepare_token_transfers(transfers: list[str]) -> list[TokenTransfer]: return token_transfers -def set_proxy_from_config_if_not_provided(args: Any, env: MxpyEnv) -> None: +def set_proxy_from_config_if_not_provided(args: Any) -> None: """This function modifies the `args` object by setting the proxy from the config if not already set. If proxy is not needed (chainID and nonce are provided), the proxy will not be set.""" if not args.proxy: if hasattr(args, "chain") and args.chain and hasattr(args, "nonce") and args.nonce is not None: return + env = MxpyEnv.from_active_env() if env.proxy_url: logger.info(f"Using proxy URL from config: {env.proxy_url}") args.proxy = env.proxy_url diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 07b7e9f6..8f5f9734 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -17,7 +17,6 @@ from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers -from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.errors import BadUsage, IncorrectWalletError, NoWalletProvided from multiversx_sdk_cli.transactions import ( TransactionsController, @@ -98,8 +97,7 @@ def _add_common_arguments(args: list[str], sub: Any): def create_transaction(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_nonce_args(args) validate_receiver_args(args) @@ -143,8 +141,7 @@ def create_transaction(args: Any): def send_transaction(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_proxy_argument(args) @@ -155,7 +152,7 @@ def send_transaction(args: Any): proxy = ProxyNetworkProvider(url=args.proxy, config=config) try: - cli_shared._confirm_continuation_if_required(cli_config, tx) + cli_shared._confirm_continuation_if_required(tx) tx_hash = proxy.send_transaction(tx) output.set_emitted_transaction_hash(tx_hash.hex()) @@ -165,8 +162,7 @@ def send_transaction(args: Any): def sign_transaction(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_broadcast_args(args) @@ -207,8 +203,7 @@ def sign_transaction(args: Any): def relay_transaction(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) ensure_relayer_wallet_args_are_provided(args) validate_broadcast_args(args) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 9d6ebeba..2a01d60b 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -11,7 +11,6 @@ validate_nonce_args, validate_receiver_args, ) -from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.validators import ValidatorsController @@ -219,8 +218,7 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def do_unstake(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) @@ -250,8 +248,7 @@ def do_unstake(args: Any): def do_unjail(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) @@ -281,8 +278,7 @@ def do_unjail(args: Any): def do_unbond(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) @@ -312,8 +308,7 @@ def do_unbond(args: Any): def change_reward_address(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) @@ -343,8 +338,7 @@ def change_reward_address(args: Any): def do_claim(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) @@ -372,8 +366,7 @@ def do_claim(args: Any): def do_unstake_nodes(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) @@ -404,8 +397,7 @@ def do_unstake_nodes(args: Any): def do_unstake_tokens(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) @@ -435,8 +427,7 @@ def do_unstake_tokens(args: Any): def do_unbond_nodes(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) @@ -467,8 +458,7 @@ def do_unbond_nodes(args: Any): def do_unbond_tokens(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) @@ -498,8 +488,7 @@ def do_unbond_tokens(args: Any): def do_clean_registered_data(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) @@ -527,8 +516,7 @@ def do_clean_registered_data(args: Any): def do_restake_unstaked_nodes(args: Any): - cli_config = MxpyEnv.from_active_env() - cli_shared.set_proxy_from_config_if_not_provided(args, cli_config) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_args(args) sender = cli_shared.prepare_sender(args) diff --git a/multiversx_sdk_cli/tests/test_cli_governance.py b/multiversx_sdk_cli/tests/test_cli_governance.py index c67922d2..65887c12 100644 --- a/multiversx_sdk_cli/tests/test_cli_governance.py +++ b/multiversx_sdk_cli/tests/test_cli_governance.py @@ -209,4 +209,5 @@ def _read_stdout(capsys: Any) -> str: def get_transaction(capsys: Any) -> dict[str, Any]: out = _read_stdout(capsys) output: dict[str, Any] = json.loads(out) - return output["emittedTransaction"] + tx: dict[str, Any] = output["emittedTransaction"] + return tx diff --git a/multiversx_sdk_cli/tests/test_cli_multisig.py b/multiversx_sdk_cli/tests/test_cli_multisig.py index e280e867..f2788f43 100644 --- a/multiversx_sdk_cli/tests/test_cli_multisig.py +++ b/multiversx_sdk_cli/tests/test_cli_multisig.py @@ -955,4 +955,5 @@ def _read_stdout(capsys: Any) -> str: def get_transaction(capsys: Any) -> dict[str, Any]: out = _read_stdout(capsys) output: dict[str, Any] = json.loads(out) - return output["emittedTransaction"] + tx: dict[str, Any] = output["emittedTransaction"] + return tx From ff307d493e6e492fcaacb4122bb2679f9ded17c5 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 5 Jun 2025 17:36:31 +0300 Subject: [PATCH 28/64] small refactoring --- multiversx_sdk_cli/cli_governance.py | 3 +-- multiversx_sdk_cli/cli_multisig.py | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/multiversx_sdk_cli/cli_governance.py b/multiversx_sdk_cli/cli_governance.py index 337f614c..98b653f3 100644 --- a/multiversx_sdk_cli/cli_governance.py +++ b/multiversx_sdk_cli/cli_governance.py @@ -12,7 +12,6 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.args_validation import ( - ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, validate_proxy_argument, @@ -193,7 +192,7 @@ def _add_common_args(args: Any, sub: Any): def _ensure_args(args: Any): - ensure_wallet_args_are_provided(args) + cli_shared.set_proxy_from_config_if_not_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index e9ba68ae..42ea9711 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -29,7 +29,6 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.args_validation import ( - ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, validate_proxy_argument, @@ -658,7 +657,6 @@ def _ensure_args(args: Any): cli_shared.set_proxy_from_config_if_not_provided(args) validate_transaction_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) From 48d8c49ae1525bbf50647d937e1bd80c27274432 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 6 Jun 2025 16:31:52 +0300 Subject: [PATCH 29/64] wrap Ledger error --- .../base_transactions_controller.py | 17 +++++++++++++---- multiversx_sdk_cli/cli_shared.py | 16 +++++++++++++--- multiversx_sdk_cli/errors.py | 10 ++++++++++ 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/multiversx_sdk_cli/base_transactions_controller.py b/multiversx_sdk_cli/base_transactions_controller.py index 60f685db..1a90c7f2 100644 --- a/multiversx_sdk_cli/base_transactions_controller.py +++ b/multiversx_sdk_cli/base_transactions_controller.py @@ -22,7 +22,7 @@ TRUE_STR_LOWER, ) from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.errors import BadUserInput +from multiversx_sdk_cli.errors import BadUserInput, TransactionSigningError from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount @@ -47,7 +47,10 @@ def sign_transaction( self._set_options_for_hash_signing_if_needed(transaction, sender, guardian, relayer) if sender: - transaction.signature = sender.sign_transaction(transaction) + try: + transaction.signature = sender.sign_transaction(transaction) + except Exception as e: + raise TransactionSigningError(f"Could not sign transaction: {str(e)}") self._sign_guarded_transaction_if_guardian( transaction, @@ -94,7 +97,10 @@ def _sign_guarded_transaction_if_guardian( ) -> Transaction: # If the guardian account is provided, we sign locally. Otherwise, we reach for the trusted cosign service. if guardian: - transaction.guardian_signature = guardian.sign_transaction(transaction) + try: + transaction.guardian_signature = guardian.sign_transaction(transaction) + except Exception as e: + raise TransactionSigningError(f"Could not sign transaction: {str(e)}") elif transaction.guardian and guardian_service_url and guardian_2fa_code: cosign_transaction(transaction, guardian_service_url, guardian_2fa_code) @@ -102,7 +108,10 @@ def _sign_guarded_transaction_if_guardian( def _sign_relayed_transaction_if_relayer(self, transaction: Transaction, relayer: Union[IAccount, None]): if relayer and transaction.relayer: - transaction.relayer_signature = relayer.sign_transaction(transaction) + try: + transaction.relayer_signature = relayer.sign_transaction(transaction) + except Exception as e: + raise TransactionSigningError(f"Could not sign transaction: {str(e)}") def _convert_args_to_typed_values(self, arguments: list[str]) -> list[Any]: args: list[Any] = [] diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index d833d6ff..283df25e 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -33,6 +33,7 @@ ArgumentsNotProvidedError, BadUsage, IncorrectWalletError, + LedgerError, ) from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount @@ -346,7 +347,10 @@ def prepare_account(args: Any): hrp=hrp, ) elif args.ledger: - return LedgerAccount(address_index=args.sender_wallet_index) + try: + return LedgerAccount(address_index=args.sender_wallet_index) + except Exception as e: + raise LedgerError(str(e)) else: raise errors.NoWalletProvided() @@ -402,7 +406,10 @@ def load_guardian_account(args: Any) -> Union[IAccount, None]: hrp=hrp, ) elif args.guardian_ledger: - return LedgerAccount(address_index=args.guardian_wallet_index) + try: + return LedgerAccount(address_index=args.guardian_wallet_index) + except Exception as e: + raise LedgerError(str(e)) return None @@ -533,7 +540,10 @@ def load_relayer_account(args: Any) -> Union[IAccount, None]: hrp=hrp, ) elif args.relayer_ledger: - return LedgerAccount(address_index=args.relayer_wallet_index) + try: + return LedgerAccount(address_index=args.relayer_wallet_index) + except Exception as e: + raise LedgerError(str(e)) return None diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 577d6207..1ec56977 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -149,3 +149,13 @@ def __init__(self, message: str): class InvalidArgumentsError(KnownError): def __init__(self, message: str): super().__init__(message) + + +class LedgerError(KnownError): + def __init__(self, message: str): + super().__init__(message) + + +class TransactionSigningError(KnownError): + def __init__(self, message: str): + super().__init__(message) From 2a5d73b582dba6cf9623b4983254475f682dafa3 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 11 Jun 2025 15:41:53 +0300 Subject: [PATCH 30/64] fix after review --- multiversx_sdk_cli/cli.py | 2 ++ multiversx_sdk_cli/cli_contracts.py | 8 ----- multiversx_sdk_cli/cli_delegation.py | 38 ----------------------- multiversx_sdk_cli/cli_dns.py | 8 ----- multiversx_sdk_cli/cli_governance.py | 13 +++----- multiversx_sdk_cli/cli_multisig.py | 43 +++++++++++--------------- multiversx_sdk_cli/cli_shared.py | 3 ++ multiversx_sdk_cli/cli_transactions.py | 8 ----- multiversx_sdk_cli/cli_validators.py | 33 +++++++------------- 9 files changed, 38 insertions(+), 118 deletions(-) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index c52dae5f..b41797bc 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -27,6 +27,7 @@ import multiversx_sdk_cli.cli_wallet import multiversx_sdk_cli.version from multiversx_sdk_cli import config, errors, utils, ux +from multiversx_sdk_cli.cli_shared import set_proxy_from_config_if_not_provided from multiversx_sdk_cli.env import get_address_hrp logger = logging.getLogger("cli") @@ -77,6 +78,7 @@ def _do_main(cli_args: list[str]): if not hasattr(args, "func"): parser.print_help() else: + set_proxy_from_config_if_not_provided(args) args.func(args) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 3cb6c705..2f40d60c 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -319,8 +319,6 @@ def build(args: Any): def deploy(args: Any): logger.debug("deploy") - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -372,8 +370,6 @@ def deploy(args: Any): def call(args: Any): logger.debug("call") - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -420,8 +416,6 @@ def call(args: Any): def upgrade(args: Any): logger.debug("upgrade") - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) @@ -467,8 +461,6 @@ def upgrade(args: Any): def query(args: Any): logger.debug("query") - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_proxy_argument(args) # we don't need chainID to query a contract; we use the provided proxy diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index 73d61a0e..994a40d3 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -402,8 +402,6 @@ def _get_delegation_controller(args: Any): def do_create_delegation_contract(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -432,8 +430,6 @@ def do_create_delegation_contract(args: Any): def get_contract_address_by_deploy_tx_hash(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_proxy_argument(args) config = get_config_for_network_providers() @@ -451,8 +447,6 @@ def get_contract_address_by_deploy_tx_hash(args: Any): def add_new_nodes(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -501,8 +495,6 @@ def _get_public_keys_and_signed_messages(args: Any) -> tuple[list[ValidatorPubli def remove_nodes(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) _check_if_either_bls_keys_or_validators_file_are_provided(args) @@ -552,8 +544,6 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def stake_nodes(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -592,8 +582,6 @@ def _check_if_either_bls_keys_or_validators_file_are_provided(args: Any): def unbond_nodes(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -624,8 +612,6 @@ def unbond_nodes(args: Any): def unstake_nodes(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -656,8 +642,6 @@ def unstake_nodes(args: Any): def unjail_nodes(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - _check_if_either_bls_keys_or_validators_file_are_provided(args) validate_arguments(args) @@ -688,8 +672,6 @@ def unjail_nodes(args: Any): def delegate(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -718,8 +700,6 @@ def delegate(args: Any): def claim_rewards(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -746,8 +726,6 @@ def claim_rewards(args: Any): def redelegate_rewards(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -774,8 +752,6 @@ def redelegate_rewards(args: Any): def undelegate(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -804,8 +780,6 @@ def undelegate(args: Any): def withdraw(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -833,8 +807,6 @@ def withdraw(args: Any): def change_service_fee(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -863,8 +835,6 @@ def change_service_fee(args: Any): def modify_delegation_cap(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -893,8 +863,6 @@ def modify_delegation_cap(args: Any): def automatic_activation(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -924,8 +892,6 @@ def automatic_activation(args: Any): def redelegate_cap(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -955,8 +921,6 @@ def redelegate_cap(args: Any): def set_metadata(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) @@ -987,8 +951,6 @@ def set_metadata(args: Any): def make_new_contract_from_validator_data(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_arguments(args) sender = cli_shared.prepare_sender(args) diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index 734dde82..2568a06d 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -136,8 +136,6 @@ def _ensure_proxy_is_provided(args: Any): def dns_resolve(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - _ensure_proxy_is_provided(args) config = get_config_for_network_providers() @@ -147,8 +145,6 @@ def dns_resolve(args: Any): def dns_validate_name(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - _ensure_proxy_is_provided(args) config = get_config_for_network_providers() @@ -172,8 +168,6 @@ def get_dns_address_for_name_hex(args: Any): def get_registration_cost(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - _ensure_proxy_is_provided(args) config = get_config_for_network_providers() @@ -181,8 +175,6 @@ def get_registration_cost(args: Any): def get_version(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - _ensure_proxy_is_provided(args) config = get_config_for_network_providers() diff --git a/multiversx_sdk_cli/cli_governance.py b/multiversx_sdk_cli/cli_governance.py index 337f614c..88798f3a 100644 --- a/multiversx_sdk_cli/cli_governance.py +++ b/multiversx_sdk_cli/cli_governance.py @@ -198,11 +198,6 @@ def _ensure_args(args: Any): validate_chain_id_args(args) -def _ensure_proxy_arg(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_proxy_argument(args) - - def create_proposal(args: Any): _ensure_args(args) @@ -368,7 +363,7 @@ def change_config(args: Any): def get_voting_power(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) config = get_config_for_network_providers() proxy = ProxyNetworkProvider(url=args.proxy, config=config) @@ -382,7 +377,7 @@ def get_voting_power(args: Any): def get_config(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) config = get_config_for_network_providers() proxy = ProxyNetworkProvider(url=args.proxy, config=config) @@ -394,7 +389,7 @@ def get_config(args: Any): def get_proposal(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) config = get_config_for_network_providers() proxy = ProxyNetworkProvider(url=args.proxy, config=config) @@ -406,7 +401,7 @@ def get_proposal(args: Any): def get_delegated_vote_info(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) config = get_config_for_network_providers() proxy = ProxyNetworkProvider(url=args.proxy, config=config) diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index e9ba68ae..4bf03fc3 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -655,19 +655,12 @@ def _add_common_args(args: Any, sub: Any, with_contract_arg: bool = True, with_r def _ensure_args(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_transaction_args(args) ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) -def _ensure_proxy_arg(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_proxy_argument(args) - - def deploy(args: Any): logger.debug("multisig.deploy") _ensure_args(args) @@ -1438,7 +1431,7 @@ def perform_batch(args: Any): def get_quorum(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1451,7 +1444,7 @@ def get_quorum(args: Any): def get_num_board_members(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1464,7 +1457,7 @@ def get_num_board_members(args: Any): def get_num_groups(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1477,7 +1470,7 @@ def get_num_groups(args: Any): def get_num_proposers(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1490,7 +1483,7 @@ def get_num_proposers(args: Any): def get_action_group(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1503,7 +1496,7 @@ def get_action_group(args: Any): def get_last_group_action_id(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1516,7 +1509,7 @@ def get_last_group_action_id(args: Any): def get_action_last_index(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1529,7 +1522,7 @@ def get_action_last_index(args: Any): def is_signed_by(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1546,7 +1539,7 @@ def is_signed_by(args: Any): def is_quorum_reached(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1562,7 +1555,7 @@ def is_quorum_reached(args: Any): def get_pending_actions_full_info(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1578,7 +1571,7 @@ def get_pending_actions_full_info(args: Any): def get_user_role(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1594,7 +1587,7 @@ def get_user_role(args: Any): def get_all_board_members(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1614,7 +1607,7 @@ def get_all_board_members(args: Any): def get_all_proposers(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1634,7 +1627,7 @@ def get_all_proposers(args: Any): def get_action_data(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1650,7 +1643,7 @@ def get_action_data(args: Any): def get_action_signers(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1668,7 +1661,7 @@ def get_action_signers(args: Any): def get_action_signer_count(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1684,7 +1677,7 @@ def get_action_signer_count(args: Any): def get_action_valid_signer_count(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() @@ -1700,7 +1693,7 @@ def get_action_valid_signer_count(args: Any): def parse_proposal(args: Any): - _ensure_proxy_arg(args) + validate_proxy_argument(args) abi = Abi.load(Path(args.abi)) config = get_config_for_network_providers() diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index bdb87e77..e21ec67c 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -716,6 +716,9 @@ def prepare_token_transfers(transfers: list[str]) -> list[TokenTransfer]: def set_proxy_from_config_if_not_provided(args: Any) -> None: """This function modifies the `args` object by setting the proxy from the config if not already set. If proxy is not needed (chainID and nonce are provided), the proxy will not be set.""" + if not hasattr(args, "proxy"): + return + if not args.proxy: if hasattr(args, "chain") and args.chain and hasattr(args, "nonce") and args.nonce is not None: return diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 8f5f9734..ccd65068 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -97,8 +97,6 @@ def _add_common_arguments(args: list[str], sub: Any): def create_transaction(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_nonce_args(args) validate_receiver_args(args) ensure_wallet_args_are_provided(args) @@ -141,8 +139,6 @@ def create_transaction(args: Any): def send_transaction(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_proxy_argument(args) tx = load_transaction_from_file(args.infile) @@ -162,8 +158,6 @@ def send_transaction(args: Any): def sign_transaction(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_broadcast_args(args) tx = load_transaction_from_file(args.infile) @@ -203,8 +197,6 @@ def sign_transaction(args: Any): def relay_transaction(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - ensure_relayer_wallet_args_are_provided(args) validate_broadcast_args(args) diff --git a/multiversx_sdk_cli/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 2a01d60b..32a8532e 100644 --- a/multiversx_sdk_cli/cli_validators.py +++ b/multiversx_sdk_cli/cli_validators.py @@ -218,9 +218,8 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def do_unstake(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -248,9 +247,8 @@ def do_unstake(args: Any): def do_unjail(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -278,9 +276,8 @@ def do_unjail(args: Any): def do_unbond(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -308,9 +305,8 @@ def do_unbond(args: Any): def change_reward_address(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -338,9 +334,8 @@ def change_reward_address(args: Any): def do_claim(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -366,9 +361,8 @@ def do_claim(args: Any): def do_unstake_nodes(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -397,9 +391,8 @@ def do_unstake_nodes(args: Any): def do_unstake_tokens(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -427,9 +420,8 @@ def do_unstake_tokens(args: Any): def do_unbond_nodes(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -458,9 +450,8 @@ def do_unbond_nodes(args: Any): def do_unbond_tokens(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -488,9 +479,8 @@ def do_unbond_tokens(args: Any): def do_clean_registered_data(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -516,9 +506,8 @@ def do_clean_registered_data(args: Any): def do_restake_unstaked_nodes(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) - validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), From d0ee70a96a1182bb5d517a7ec0cd07ac5478651d Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 12 Jun 2025 13:26:33 +0300 Subject: [PATCH 31/64] set permissions for workflows --- .github/workflows/build-windows.yml | 3 +++ .github/workflows/build.yml | 3 +++ .github/workflows/install-macos-pipx.yml | 3 +++ .github/workflows/install-ubuntu-pipx.yml | 3 +++ .github/workflows/mypy.yml | 4 ++++ .github/workflows/test-localnet-tests.yml | 7 +++++-- .github/workflows/test-localnet.yml | 13 ++++++++----- 7 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index fda48f40..32c1c314 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -8,6 +8,9 @@ on: branches: [main, feat/*] workflow_dispatch: +permissions: + contents: read + jobs: build: name: Build and Test mxpy for ${{ matrix.os }}, python ${{ matrix.python-version }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f31f431..c84533cd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,9 @@ on: branches: [main, feat/*] workflow_dispatch: +permissions: + contents: read + jobs: build: name: Build and Test mxpy for ${{ matrix.os }}, python ${{ matrix.python-version }} diff --git a/.github/workflows/install-macos-pipx.yml b/.github/workflows/install-macos-pipx.yml index 7090ee06..1c451760 100644 --- a/.github/workflows/install-macos-pipx.yml +++ b/.github/workflows/install-macos-pipx.yml @@ -5,6 +5,9 @@ on: branches: [main, feat/*] workflow_dispatch: +permissions: + contents: read + env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} diff --git a/.github/workflows/install-ubuntu-pipx.yml b/.github/workflows/install-ubuntu-pipx.yml index 89c4afa4..9a94e492 100644 --- a/.github/workflows/install-ubuntu-pipx.yml +++ b/.github/workflows/install-ubuntu-pipx.yml @@ -5,6 +5,9 @@ on: branches: [main, feat/*] workflow_dispatch: +permissions: + contents: read + env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index a82a8196..559f2530 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -1,5 +1,9 @@ name: mypy reviewdog check on: [pull_request] + +permissions: + contents: read + jobs: mypy: name: runner / mypy diff --git a/.github/workflows/test-localnet-tests.yml b/.github/workflows/test-localnet-tests.yml index 8c62ad80..5f64fcc8 100644 --- a/.github/workflows/test-localnet-tests.yml +++ b/.github/workflows/test-localnet-tests.yml @@ -5,6 +5,9 @@ on: branches: [main, feat/*] workflow_dispatch: +permissions: + contents: read + env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} @@ -40,10 +43,10 @@ jobs: export PYTHONPATH=. python3 -m multiversx_sdk_cli.cli localnet prerequisites --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml python3 -m multiversx_sdk_cli.cli localnet build --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml - + # "Go" and artifacts from "GOPATH/pkg/mod" are not needed anymore. sudo rm -rf ~/multiversx-sdk/golang - + python3 -m multiversx_sdk_cli.cli localnet clean --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml python3 -m multiversx_sdk_cli.cli localnet config --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml nohup python3 -m multiversx_sdk_cli.cli localnet start --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml > localnet.log 2>&1 & echo $! > localnet.pid diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index dcbc9a4f..9f3f063d 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -5,6 +5,9 @@ on: branches: [main, feat/*] workflow_dispatch: +permissions: + contents: read + env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} @@ -40,10 +43,10 @@ jobs: python3 -m multiversx_sdk_cli.cli config set github_api_token ${{ secrets.GITHUB_TOKEN }} python3 -m multiversx_sdk_cli.cli localnet prerequisites --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml python3 -m multiversx_sdk_cli.cli localnet build --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml - + # "Go" and artifacts from "GOPATH/pkg/mod" are not needed anymore. sudo rm -rf ~/multiversx-sdk/golang - + python3 -m multiversx_sdk_cli.cli localnet clean --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml python3 -m multiversx_sdk_cli.cli localnet config --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml python3 -m multiversx_sdk_cli.cli localnet start --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml --stop-after-seconds=120 @@ -62,13 +65,13 @@ jobs: git clone https://github.com/multiversx/mx-chain-go --branch=master --single-branch ~/multiversx-sdk/sandbox/mx-chain-go git clone https://github.com/multiversx/mx-chain-proxy-go --branch=master --single-branch ~/multiversx-sdk/sandbox/mx-chain-proxy-go - + python3 -m multiversx_sdk_cli.cli localnet prerequisites --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_local.toml python3 -m multiversx_sdk_cli.cli localnet build --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_local.toml - + # "Go" and artifacts from "GOPATH/pkg/mod" are not needed anymore. sudo rm -rf ~/multiversx-sdk/golang - + python3 -m multiversx_sdk_cli.cli localnet clean --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_local.toml python3 -m multiversx_sdk_cli.cli localnet config --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_local.toml python3 -m multiversx_sdk_cli.cli localnet start --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_local.toml --stop-after-seconds=120 From 28c827d55a1fbaa44dfb88251971a343b823dd48 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 12 Jun 2025 14:22:10 +0300 Subject: [PATCH 32/64] update to latest sdk-py --- pyproject.toml | 2 +- requirements.txt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a6f06f85..859ea6ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ dependencies = [ "ledgercomm[hid]", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk[ledger]==1.2.0" + "multiversx-sdk[ledger]==1.6.0" ] [project.scripts] diff --git a/requirements.txt b/requirements.txt index 4694973a..3db43681 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,5 +6,4 @@ ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 -# multiversx-sdk[ledger]==1.2.0 -git+https://github.com/multiversx/mx-sdk-py.git@feat/next#egg=multiversx-sdk +multiversx-sdk[ledger]==1.6.0 From cddc41504fec331996e0fe086c7e5260d2efb866 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 12 Jun 2025 14:31:17 +0300 Subject: [PATCH 33/64] remove unnecessary function call --- multiversx_sdk_cli/cli_governance.py | 1 - 1 file changed, 1 deletion(-) diff --git a/multiversx_sdk_cli/cli_governance.py b/multiversx_sdk_cli/cli_governance.py index 4318c702..c9ebbbcf 100644 --- a/multiversx_sdk_cli/cli_governance.py +++ b/multiversx_sdk_cli/cli_governance.py @@ -192,7 +192,6 @@ def _add_common_args(args: Any, sub: Any): def _ensure_args(args: Any): - cli_shared.set_proxy_from_config_if_not_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) From 20034faef3ec57f0344515e30be981ba003f27f3 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 12 Jun 2025 15:24:07 +0300 Subject: [PATCH 34/64] remove recall nonce argument --- CLI.md | 124 ------------------------------- multiversx_sdk_cli/cli.py | 3 - multiversx_sdk_cli/cli_shared.py | 6 -- 3 files changed, 133 deletions(-) diff --git a/CLI.md b/CLI.md index 9a81917a..d7caf3e9 100644 --- a/CLI.md +++ b/CLI.md @@ -140,8 +140,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -235,8 +233,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -340,8 +336,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -523,8 +517,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --receiver RECEIVER 🖄 the address of the receiver --receiver-username RECEIVER_USERNAME 🖄 the username of the receiver --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) @@ -747,8 +739,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -805,8 +795,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -861,8 +849,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -917,8 +903,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -973,8 +957,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1029,8 +1011,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1084,8 +1064,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1140,8 +1118,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1196,8 +1172,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1252,8 +1226,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1308,8 +1280,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1363,8 +1333,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1458,8 +1426,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1532,8 +1498,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1590,8 +1554,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1648,8 +1610,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1706,8 +1666,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1764,8 +1722,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1822,8 +1778,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1878,8 +1832,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1934,8 +1886,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -1990,8 +1940,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -2046,8 +1994,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -2102,8 +2048,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -2159,8 +2103,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -2216,8 +2158,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -2274,8 +2214,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -2332,8 +2270,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -2391,8 +2327,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -2450,8 +2384,6 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -3225,8 +3157,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -3302,8 +3232,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -3377,8 +3305,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -3451,8 +3377,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -3525,8 +3449,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -3599,8 +3521,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -3673,8 +3593,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -3748,8 +3666,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -3830,8 +3746,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --receiver RECEIVER 🖄 the address of the receiver --receiver-username RECEIVER_USERNAME 🖄 the username of the receiver --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) @@ -3917,8 +3831,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --receiver RECEIVER 🖄 the address of the receiver --receiver-username RECEIVER_USERNAME 🖄 the username of the receiver --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) @@ -4005,8 +3917,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --receiver RECEIVER 🖄 the address of the receiver --receiver-username RECEIVER_USERNAME 🖄 the username of the receiver --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) @@ -4092,8 +4002,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -4177,8 +4085,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -4251,8 +4157,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -4325,8 +4229,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -4399,8 +4301,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -4473,8 +4373,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -4547,8 +4445,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -4621,8 +4517,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -4697,8 +4591,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -4772,8 +4664,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -4846,8 +4736,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -5480,8 +5368,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -5553,8 +5439,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -5625,8 +5509,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -5698,8 +5580,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -5769,8 +5649,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) @@ -5845,8 +5723,6 @@ options: --proxy PROXY 🔗 the URL of the proxy --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the network. - --recall-nonce ⭮ whether to recall the nonce when creating the transaction (default: - False). This argument is OBSOLETE. --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) --gas-limit GAS_LIMIT ⛽ the gas limit --value VALUE the value to transfer (default: 0) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index b81ee9b8..c9c14b88 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -74,9 +74,6 @@ def _do_main(cli_args: list[str]): default_hrp = get_address_hrp() LibraryConfig.default_address_hrp = default_hrp - if hasattr(args, "recall_nonce") and args.recall_nonce: - logger.warning("The --recall-nonce flag is DEPRECATED. The nonce is fetched from the network by default.") - if not hasattr(args, "func"): parser.print_help() else: diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 5c4073d1..60794ac3 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -126,12 +126,6 @@ def add_tx_args( default=None, help="# the nonce for the transaction. If not provided, is fetched from the network.", ) - sub.add_argument( - "--recall-nonce", - action="store_true", - default=False, - help="⭮ whether to recall the nonce when creating the transaction (default: %(default)s). This argument is OBSOLETE.", - ) if with_receiver: sub.add_argument("--receiver", required=False, help="🖄 the address of the receiver") From f48ed733b5f6067e892deb1eb91dce2ca7cfa571 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 12 Jun 2025 15:32:15 +0300 Subject: [PATCH 35/64] fix mypyp error --- multiversx_sdk_cli/cli_shared.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index ed617d73..74328a2a 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -404,10 +404,10 @@ def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Accoun if kind not in ["pem", "keystore"]: raise InvalidAddressConfigValue("'kind' must be 'pem' or 'keystore'") - path = wallet.get("path", None) - if not path: + wallet_path = wallet.get("path", None) + if not wallet_path: raise AddressConfigFileError("'path' field must be set in the address config") - path = Path(path) + path = Path(wallet_path) index = int(wallet.get("index", 0)) From d1484017c014e8c631a1ce2e1d84067f1c8ee8bb Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 13 Jun 2025 10:10:42 +0300 Subject: [PATCH 36/64] set log level from config or cli args --- multiversx_sdk_cli/cli.py | 22 +++++++++++++++++++++- multiversx_sdk_cli/config.py | 9 +++++++++ multiversx_sdk_cli/errors.py | 5 +++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index c9c14b88..e626debb 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -53,6 +53,7 @@ def _do_main(cli_args: list[str]): parser = setup_parser(cli_args) argcomplete.autocomplete(parser) + _handle_log_level_argument(cli_args) _handle_verbose_argument(cli_args) args = parser.parse_args(cli_args) @@ -64,8 +65,9 @@ def _do_main(cli_args: list[str]): handlers=[RichHandler(show_time=False, rich_tracebacks=True)], ) else: + level: str = args.log_level logging.basicConfig( - level="INFO", + level=level.upper(), format="%(name)s: %(message)s", handlers=[RichHandler(show_time=False, rich_tracebacks=True)], ) @@ -111,6 +113,13 @@ def setup_parser(args: list[str]): version=f"MultiversX Python CLI (mxpy) {version}", ) parser.add_argument("--verbose", action="store_true", default=False) + parser.add_argument( + "--log-level", + type=str, + default=config.get_log_level_from_config(), + choices=["debug", "info", "warning", "error"], + help="default: %(default)s", + ) subparsers = parser.add_subparsers() commands: list[Any] = [] @@ -165,6 +174,17 @@ def _handle_verbose_argument(args: list[str]): args.insert(0, verbose_arg) +def _handle_log_level_argument(args: list[str]): + log_level_arg = "--log-level" + + if log_level_arg in args: + index = args.index(log_level_arg) + log_arg = args.pop(index) + log_value = args.pop(index) + args.insert(0, log_value) + args.insert(0, log_arg) + + if __name__ == "__main__": ret = main(sys.argv[1:]) sys.exit(ret) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 219c7770..921a28d7 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -136,9 +136,18 @@ def get_defaults() -> dict[str, Any]: "dependencies.testwallets.urlTemplate.osx": "https://github.com/multiversx/mx-sdk-testwallets/archive/{TAG}.tar.gz", "dependencies.testwallets.urlTemplate.windows": "https://github.com/multiversx/mx-sdk-testwallets/archive/{TAG}.tar.gz", "github_api_token": "", + "log_level": "info", } +def get_log_level_from_config(): + log_level = get_value("log_level") + if log_level not in ["debug", "info", "warning", "error"]: + raise errors.LogLevelError(log_level) + + return log_level + + def get_deprecated_entries_in_config_file(): default_config_keys = set(get_defaults().keys()) current_config_keys = set(get_active().keys()) diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index a7853764..60d684e9 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -211,3 +211,8 @@ def __init__(self, message: str): class AddressConfigFileError(KnownError): def __init__(self, message: str): super().__init__(message) + + +class LogLevelError(KnownError): + def __init__(self, log_level: str): + super().__init__(f"Log level not accepted: {log_level}. Choose between ['debug', 'info', 'warning', 'error'].") From 533761eea9512464e23a31da446d18d69856946a Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Fri, 13 Jun 2025 14:01:31 +0300 Subject: [PATCH 37/64] refactoring --- multiversx_sdk_cli/cli_get.py | 78 ++++++++++++++++++-------------- multiversx_sdk_cli/cli_shared.py | 50 +++++++++++--------- multiversx_sdk_cli/errors.py | 5 ++ 3 files changed, 78 insertions(+), 55 deletions(-) diff --git a/multiversx_sdk_cli/cli_get.py b/multiversx_sdk_cli/cli_get.py index e59ebaee..af335d65 100644 --- a/multiversx_sdk_cli/cli_get.py +++ b/multiversx_sdk_cli/cli_get.py @@ -1,22 +1,17 @@ import logging from typing import Any, Optional -from multiversx_sdk import Address, ProxyNetworkProvider, Token, TokenComputer +from multiversx_sdk import Address +from multiversx_sdk import NetworkProviderError as SDKNetworkProviderError +from multiversx_sdk import ProxyNetworkProvider, Token, TokenComputer from multiversx_sdk_cli import cli_shared -from multiversx_sdk_cli.address import ( - get_active_address, - read_address_config_file, - resolve_address_config_path, -) from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.errors import ( - AddressConfigFileError, ArgumentsNotProvidedError, BadUsage, - NoWalletProvided, - UnknownAddressAliasError, + NetworkProviderError, ) from multiversx_sdk_cli.utils import dump_out_json @@ -74,6 +69,21 @@ def setup_parser(subparsers: Any) -> Any: sub.add_argument("--hash", type=str, required=True, help="the transaction hash") sub.set_defaults(func=get_transaction) + sub = cli_shared.add_command_subparser(subparsers, "get", "network-config", "Get the network configuration.") + _add_proxy_arg(sub) + sub.set_defaults(func=get_network_config) + + sub = cli_shared.add_command_subparser(subparsers, "get", "network-status", "Get the network status.") + _add_proxy_arg(sub) + sub.add_argument( + "--shard", + type=int, + choices=[0, 1, 2, 4294967295], + default=4294967295, + help="the shard to get the status for (default: %(default)s, which is methachain)", + ) + sub.set_defaults(func=get_network_status) + parser.epilog = cli_shared.build_group_epilog(subparsers) return subparsers @@ -142,7 +152,10 @@ def get_key(args: Any): proxy = _get_proxy(args) logger.info(f"Fetching details about {address.to_bech32()}") - response = proxy.get_account_storage_entry(address, args.key) + try: + response = proxy.get_account_storage_entry(address, args.key) + except SDKNetworkProviderError as e: + raise NetworkProviderError(e.url, e.data) dump_out_json(response.raw) @@ -173,39 +186,36 @@ def get_token(args: Any): def get_transaction(args: Any): proxy = _get_proxy(args) - response = proxy.get_transaction(args.hash) + try: + response = proxy.get_transaction(args.hash) + except SDKNetworkProviderError as e: + raise NetworkProviderError(e.url, e.data) + except Exception as e: + raise NetworkProviderError("", str(e)) dump_out_json(response.raw) -def _get_address_from_alias_or_config(alias: Optional[str], hrp: str) -> Address: - if alias: - file_path = resolve_address_config_path() - if not file_path.is_file(): - raise AddressConfigFileError("The address config file was not found") +def get_network_config(args: Any): + proxy = _get_proxy(args) + config = proxy.get_network_config() - file = read_address_config_file() - if file == dict(): - raise AddressConfigFileError("Address config file is empty") + dump_out_json(config.raw) - addresses: dict[str, Any] = file["addresses"] - wallet = addresses.get(alias, None) - if not wallet: - raise UnknownAddressAliasError(alias) - logger.info(f"Using address of [{alias}] from address config.") - account = cli_shared.load_wallet_from_address_config(wallet=wallet, hrp=hrp) - return account.address - else: - active_address = get_active_address() - if active_address == dict(): - logger.info("No default wallet found in address config.") - raise NoWalletProvided() +def get_network_status(args: Any): + proxy = _get_proxy(args) + status = proxy.get_network_status() + + dump_out_json(status.raw) - alias_of_default_wallet = read_address_config_file().get("active", "") - logger.info(f"Using address of [{alias_of_default_wallet}] from address config.") - account = cli_shared.load_wallet_from_address_config(wallet=active_address, hrp=hrp) +def _get_address_from_alias_or_config(alias: Optional[str], hrp: str) -> Address: + if alias: + account = cli_shared.load_wallet_by_alias(alias=alias, hrp=hrp) + return account.address + else: + account = cli_shared.load_default_wallet(hrp=hrp) return account.address diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 5c4073d1..9f30d534 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -369,34 +369,42 @@ def prepare_account(args: Any): except Exception as e: raise LedgerError(str(e)) elif args.sender: - file_path = resolve_address_config_path() - if not file_path.is_file(): - raise AddressConfigFileError("The address config file was not found") + return load_wallet_by_alias(alias=args.sender, hrp=hrp) + else: + return load_default_wallet(hrp=hrp) - file = read_address_config_file() - if file == dict(): - raise AddressConfigFileError("Address config file is empty") - addresses: dict[str, Any] = file["addresses"] - wallet = addresses.get(args.sender, None) - if not wallet: - raise UnknownAddressAliasError(args.sender) +def load_wallet_by_alias(alias: str, hrp: str) -> Account: + file_path = resolve_address_config_path() + if not file_path.is_file(): + raise AddressConfigFileError("The address config file was not found") - logger.info(f"Using sender [{args.sender}] from address config.") - return load_wallet_from_address_config(wallet=wallet, hrp=hrp) - else: - active_address = get_active_address() - if active_address == dict(): - logger.info("No default wallet found in address config.") - raise NoWalletProvided() + file = read_address_config_file() + if file == dict(): + raise AddressConfigFileError("Address config file is empty") + + addresses: dict[str, Any] = file["addresses"] + wallet = addresses.get(alias, None) + if not wallet: + raise UnknownAddressAliasError(alias) + + logger.info(f"Using sender [{alias}] from address config.") + return _load_wallet_from_address_config(wallet=wallet, hrp=hrp) + + +def load_default_wallet(hrp: str) -> Account: + active_address = get_active_address() + if active_address == dict(): + logger.info("No default wallet found in address config.") + raise NoWalletProvided() - alias_of_default_wallet = read_address_config_file().get("active", "") - logger.info(f"Using sender [{alias_of_default_wallet}] from address config.") + alias_of_default_wallet = read_address_config_file().get("active", "") + logger.info(f"Using sender [{alias_of_default_wallet}] from address config.") - return load_wallet_from_address_config(wallet=active_address, hrp=hrp) + return _load_wallet_from_address_config(wallet=active_address, hrp=hrp) -def load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Account: +def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Account: kind = wallet.get("kind", None) if not kind: raise AddressConfigFileError("'kind' field must be set in the address config") diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index a7853764..dad55326 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -211,3 +211,8 @@ def __init__(self, message: str): class AddressConfigFileError(KnownError): def __init__(self, message: str): super().__init__(message) + + +class NetworkProviderError(KnownError): + def __init__(self, url: str, error: str): + super().__init__(f"Url = [{url}], error = {error}") From 672c0f031ef529e79b365f5e2b89750b1e09b796 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 16 Jun 2025 13:11:41 +0300 Subject: [PATCH 38/64] fix help text for opt gas limit arg --- multiversx_sdk_cli/cli_multisig.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index 69037b7e..efd3202a 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -189,7 +189,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--opt-gas-limit", type=int, - help="the size of the new quorum (number of signatures required to approve a proposal)", + help="optional gas limit for the async call", ) sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") sub.add_argument("--function", type=str, help="the function to call") @@ -208,7 +208,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--opt-gas-limit", type=int, - help="the size of the new quorum (number of signatures required to approve a proposal)", + help="optional gas limit for the async call", ) sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") sub.add_argument("--function", type=str, help="the function to call") @@ -227,7 +227,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: sub.add_argument( "--opt-gas-limit", type=int, - help="the size of the new quorum (number of signatures required to approve a proposal)", + help="optional gas limit for the async call", ) sub.add_argument("--contract-abi", type=str, help="the ABI file of the contract to call") sub.add_argument("--function", type=str, help="the function to call") From 8c600d6c75153d6ed0df6564d23a0486dbfc1c53 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 16 Jun 2025 16:08:43 +0300 Subject: [PATCH 39/64] fixes after review --- .../{address.py => address_config.py} | 40 ++++++++-- multiversx_sdk_cli/cli.py | 3 +- multiversx_sdk_cli/cli_address.py | 2 +- multiversx_sdk_cli/cli_shared.py | 12 +-- multiversx_sdk_cli/cli_transactions.py | 3 +- multiversx_sdk_cli/config.py | 2 +- multiversx_sdk_cli/constants.py | 4 + multiversx_sdk_cli/env.py | 2 +- .../tests/test_cli_default_wallet.py | 75 ++++++++++++++----- multiversx_sdk_cli/workstation.py | 5 +- 10 files changed, 108 insertions(+), 40 deletions(-) rename multiversx_sdk_cli/{address.py => address_config.py} (75%) diff --git a/multiversx_sdk_cli/address.py b/multiversx_sdk_cli/address_config.py similarity index 75% rename from multiversx_sdk_cli/address.py rename to multiversx_sdk_cli/address_config.py index 1dcebcf7..1920348b 100644 --- a/multiversx_sdk_cli/address.py +++ b/multiversx_sdk_cli/address_config.py @@ -2,6 +2,7 @@ from pathlib import Path from typing import Any +from multiversx_sdk_cli.constants import SDK_PATH from multiversx_sdk_cli.errors import ( AliasAlreadyExistsError, AliasProtectedError, @@ -10,12 +11,32 @@ ) from multiversx_sdk_cli.utils import read_json_file, write_json_file -SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() LOCAL_ADDRESS_CONFIG_PATH = Path("addresses.mxpy.json").resolve() GLOBAL_ADDRESS_CONFIG_PATH = SDK_PATH / "addresses.mxpy.json" def get_defaults() -> dict[str, str]: + """Not all values are required for a config to be valid. + + Valid config for PEM wallets: + + { + "kind": "pem", + "path": "/path/to/wallet.pem", + "index": "0", # optional, defaults to 0 + } + + Valid config for KEYSTORE wallets: + + { + "kind": "keystore", + "path": "/path/to/wallet.json", + "password": "somePassword", # if not set, passwordPath must be set + "passwordPath": "/path/to/password.txt", # if not set, password must be set + "index": "0", # optional, defaults to 0 + } + + """ return { "kind": "", "path": "", @@ -41,6 +62,7 @@ def _guard_valid_name(name: str): def get_active_address() -> dict[str, str]: + """Returns the active address configuration.""" data = read_address_config_file() addresses: dict[str, Any] = data.get("addresses", {}) active_address: str = data.get("active", "default") @@ -65,25 +87,27 @@ def resolve_address_config_path() -> Path: def set_value(name: str, value: Any): + """Sets a key-value pair in the active address config.""" _guard_valid_name(name) data = read_address_config_file() active_env = data.get("active", "default") data.setdefault("addresses", {}) data["addresses"].setdefault(active_env, {}) data["addresses"][active_env][name] = value - write_file(data) + _write_file(data) -def write_file(data: dict[str, Any]): +def _write_file(data: dict[str, Any]): env_path = resolve_address_config_path() write_json_file(str(env_path), data) def set_active(name: str): + """Switches to the address configuration with the given name.""" data = read_address_config_file() _guard_valid_address_name(data, name) data["active"] = name - write_file(data) + _write_file(data) def _guard_valid_address_name(env: Any, name: str): @@ -93,6 +117,7 @@ def _guard_valid_address_name(env: Any, name: str): def create_new_address_config(name: str, template: str): + """Creates a new address config with the given name and optional template.""" data = read_address_config_file() _guard_alias_unique(data, name) new_address = {} @@ -103,7 +128,7 @@ def create_new_address_config(name: str, template: str): data["active"] = name data.setdefault("addresses", {}) data["addresses"][name] = new_address - write_file(data) + _write_file(data) def _guard_alias_unique(env: Any, name: str): @@ -120,16 +145,17 @@ def delete_config_value(name: str): data.setdefault("addresses", {}) data["addresses"].setdefault(active_env, {}) del data["addresses"][active_env][name] - write_file(data) + _write_file(data) def delete_alias(name: str): + """Deletes the address configuration with the given name.""" _guard_valid_alias_deletion(name) data = read_address_config_file() data["addresses"].pop(name, None) if data["active"] == name: data["active"] = "default" - write_file(data) + _write_file(data) def _guard_valid_alias_deletion(name: str): diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 912685bb..21c6717c 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -29,6 +29,7 @@ import multiversx_sdk_cli.version from multiversx_sdk_cli import config, errors, utils, ux from multiversx_sdk_cli.cli_shared import set_proxy_from_config_if_not_provided +from multiversx_sdk_cli.constants import SDK_PATH from multiversx_sdk_cli.env import get_address_hrp logger = logging.getLogger("cli") @@ -48,7 +49,7 @@ def main(cli_args: list[str] = sys.argv[1:]): def _do_main(cli_args: list[str]): - utils.ensure_folder(config.SDK_PATH) + utils.ensure_folder(SDK_PATH) parser = setup_parser(cli_args) argcomplete.autocomplete(parser) diff --git a/multiversx_sdk_cli/cli_address.py b/multiversx_sdk_cli/cli_address.py index ed1f9e64..771f3a85 100644 --- a/multiversx_sdk_cli/cli_address.py +++ b/multiversx_sdk_cli/cli_address.py @@ -3,7 +3,7 @@ from typing import Any from multiversx_sdk_cli import cli_shared -from multiversx_sdk_cli.address import ( +from multiversx_sdk_cli.address_config import ( create_new_address_config, delete_alias, delete_config_value, diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 74328a2a..5be5ec67 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -21,7 +21,7 @@ ) from multiversx_sdk_cli import config, utils -from multiversx_sdk_cli.address import ( +from multiversx_sdk_cli.address_config import ( get_active_address, read_address_config_file, resolve_address_config_path, @@ -371,11 +371,11 @@ def prepare_account(args: Any): elif args.sender: file_path = resolve_address_config_path() if not file_path.is_file(): - raise AddressConfigFileError("The address config file was not found") + raise AddressConfigFileError("The address config file was not found.") file = read_address_config_file() if file == dict(): - raise AddressConfigFileError("Address config file is empty") + raise AddressConfigFileError("Address config file is empty.") addresses: dict[str, Any] = file["addresses"] wallet = addresses.get(args.sender, None) @@ -399,14 +399,14 @@ def prepare_account(args: Any): def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Account: kind = wallet.get("kind", None) if not kind: - raise AddressConfigFileError("'kind' field must be set in the address config") + raise AddressConfigFileError("'kind' field must be set in the address config.") if kind not in ["pem", "keystore"]: raise InvalidAddressConfigValue("'kind' must be 'pem' or 'keystore'") wallet_path = wallet.get("path", None) if not wallet_path: - raise AddressConfigFileError("'path' field must be set in the address config") + raise AddressConfigFileError("'path' field must be set in the address config.") path = Path(wallet_path) index = int(wallet.get("index", 0)) @@ -419,7 +419,7 @@ def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Accoun if not password and not password_path: raise AddressConfigFileError( - "'password' or 'passwordPath' must be set in the address config for keystore wallets" + "'password' or 'passwordPath' must be set in the address config for keystore wallets." ) if password_path: diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 3829ad84..7e2038e1 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -162,7 +162,8 @@ def sign_transaction(args: Any): try: sender = cli_shared.prepare_account(args) - except NoWalletProvided: + except: + logger.info("No sender wallet provided. Will not sign for the sender.") sender = None if sender and sender.address != tx.sender: diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 219c7770..3df425ef 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -6,8 +6,8 @@ from multiversx_sdk import NetworkProviderConfig from multiversx_sdk_cli import errors, utils +from multiversx_sdk_cli.constants import SDK_PATH -SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() LOCAL_CONFIG_PATH = Path("mxpy.json").resolve() GLOBAL_CONFIG_PATH = SDK_PATH / "mxpy.json" diff --git a/multiversx_sdk_cli/constants.py b/multiversx_sdk_cli/constants.py index d851f7e7..26129b8f 100644 --- a/multiversx_sdk_cli/constants.py +++ b/multiversx_sdk_cli/constants.py @@ -1,3 +1,5 @@ +from pathlib import Path + VM_TYPE_SYSTEM = "0001" VM_TYPE_WASM_VM = "0500" SC_HEX_PUBKEY_PREFIX = "0" * 16 @@ -26,3 +28,5 @@ STR_PREFIX = "str:" ADDRESS_PREFIX = "addr:" MAINCHAIN_ADDRESS_HRP = "erd" + +SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() diff --git a/multiversx_sdk_cli/env.py b/multiversx_sdk_cli/env.py index 8a878268..40f003a5 100644 --- a/multiversx_sdk_cli/env.py +++ b/multiversx_sdk_cli/env.py @@ -3,6 +3,7 @@ from pathlib import Path from typing import Any +from multiversx_sdk_cli.constants import SDK_PATH from multiversx_sdk_cli.errors import ( EnvironmentAlreadyExistsError, EnvironmentProtectedError, @@ -12,7 +13,6 @@ ) from multiversx_sdk_cli.utils import read_json_file, write_json_file -SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() LOCAL_ENV_PATH = Path("env.mxpy.json").resolve() GLOBAL_ENV_PATH = SDK_PATH / "env.mxpy.json" diff --git a/multiversx_sdk_cli/tests/test_cli_default_wallet.py b/multiversx_sdk_cli/tests/test_cli_default_wallet.py index d3f98425..0f7b5b1e 100644 --- a/multiversx_sdk_cli/tests/test_cli_default_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_default_wallet.py @@ -5,14 +5,14 @@ from multiversx_sdk_cli.cli import main -def test_empty_address_config(monkeypatch: Any, tmp_path: Path): +def test_empty_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): test_file = tmp_path / "addresses.mxpy.json" test_file.write_text("{}") - import multiversx_sdk_cli.address + import multiversx_sdk_cli.address_config - monkeypatch.setattr(multiversx_sdk_cli.address, "LOCAL_ADDRESS_CONFIG_PATH", test_file) - multiversx_sdk_cli.address.read_address_config_file.cache_clear() + monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() return_code = main( [ @@ -24,7 +24,12 @@ def test_empty_address_config(monkeypatch: Any, tmp_path: Path): "50000", ] ) + out = _read_stdout(capsys) assert return_code + assert "No wallet provided." in out + + # Clear the captured content + capsys.readouterr() return_code = main( [ @@ -39,16 +44,18 @@ def test_empty_address_config(monkeypatch: Any, tmp_path: Path): ] ) assert return_code + out = _read_stdout(capsys) + assert "Address config file is empty." in out -def test_without_address_config(monkeypatch: Any, tmp_path: Path): +def test_without_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): test_file = tmp_path / "addresses.mxpy.json" assert not test_file.exists() - import multiversx_sdk_cli.address + import multiversx_sdk_cli.address_config - monkeypatch.setattr(multiversx_sdk_cli.address, "LOCAL_ADDRESS_CONFIG_PATH", test_file) - multiversx_sdk_cli.address.read_address_config_file.cache_clear() + monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() return_code = main( [ @@ -61,6 +68,11 @@ def test_without_address_config(monkeypatch: Any, tmp_path: Path): ] ) assert return_code + out = _read_stdout(capsys) + assert "No wallet provided." in out + + # Clear the captured content + capsys.readouterr() return_code = main( [ @@ -75,9 +87,11 @@ def test_without_address_config(monkeypatch: Any, tmp_path: Path): ] ) assert return_code + out = _read_stdout(capsys) + assert "Alias is not known: invalidAlias." in out -def test_incomplete_address_config(monkeypatch: Any, tmp_path: Path): +def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): test_file = tmp_path / "addresses.mxpy.json" json_file = { "active": "alice", @@ -90,10 +104,10 @@ def test_incomplete_address_config(monkeypatch: Any, tmp_path: Path): } test_file.write_text(json.dumps(json_file)) - import multiversx_sdk_cli.address + import multiversx_sdk_cli.address_config - monkeypatch.setattr(multiversx_sdk_cli.address, "LOCAL_ADDRESS_CONFIG_PATH", test_file) - multiversx_sdk_cli.address.read_address_config_file.cache_clear() + monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() return_code = main( [ @@ -106,6 +120,11 @@ def test_incomplete_address_config(monkeypatch: Any, tmp_path: Path): ] ) assert return_code + out = _read_stdout(capsys) + assert "'kind' field must be set in the address config." in out + + # Clear the captured content + capsys.readouterr() return_code = main( [ @@ -120,6 +139,11 @@ def test_incomplete_address_config(monkeypatch: Any, tmp_path: Path): ] ) assert return_code + out = _read_stdout(capsys) + assert "'kind' field must be set in the address config." in out + + # Clear the captured content + capsys.readouterr() json_file = { "active": "alice", @@ -132,10 +156,8 @@ def test_incomplete_address_config(monkeypatch: Any, tmp_path: Path): } test_file.write_text(json.dumps(json_file)) - import multiversx_sdk_cli.address - - monkeypatch.setattr(multiversx_sdk_cli.address, "LOCAL_ADDRESS_CONFIG_PATH", test_file) - multiversx_sdk_cli.address.read_address_config_file.cache_clear() + monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() return_code = main( [ @@ -148,6 +170,11 @@ def test_incomplete_address_config(monkeypatch: Any, tmp_path: Path): ] ) assert return_code + out = _read_stdout(capsys) + assert "'path' field must be set in the address config." in out + + # Clear the captured content + capsys.readouterr() return_code = main( [ @@ -162,6 +189,11 @@ def test_incomplete_address_config(monkeypatch: Any, tmp_path: Path): ] ) assert return_code + out = _read_stdout(capsys) + assert "'path' field must be set in the address config." in out + + # Clear the captured content + capsys.readouterr() json_file = { "active": "alice", @@ -175,10 +207,8 @@ def test_incomplete_address_config(monkeypatch: Any, tmp_path: Path): } test_file.write_text(json.dumps(json_file)) - import multiversx_sdk_cli.address - - monkeypatch.setattr(multiversx_sdk_cli.address, "LOCAL_ADDRESS_CONFIG_PATH", test_file) - multiversx_sdk_cli.address.read_address_config_file.cache_clear() + monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() return_code = main( [ @@ -205,3 +235,8 @@ def test_incomplete_address_config(monkeypatch: Any, tmp_path: Path): ] ) assert return_code + + +def _read_stdout(capsys: Any) -> str: + stdout: str = capsys.readouterr().out.strip() + return stdout diff --git a/multiversx_sdk_cli/workstation.py b/multiversx_sdk_cli/workstation.py index 26c23584..52db4a11 100644 --- a/multiversx_sdk_cli/workstation.py +++ b/multiversx_sdk_cli/workstation.py @@ -1,11 +1,12 @@ import sys from pathlib import Path -from multiversx_sdk_cli import config, utils +from multiversx_sdk_cli import utils +from multiversx_sdk_cli.constants import SDK_PATH def get_tools_folder() -> Path: - folder = config.SDK_PATH + folder = SDK_PATH utils.ensure_folder(folder) return folder From d1401509da7ceba8b3792a7e6bdc223d78accd8c Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 16 Jun 2025 16:19:38 +0300 Subject: [PATCH 40/64] fix tests --- .../tests/test_cli_default_wallet.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/multiversx_sdk_cli/tests/test_cli_default_wallet.py b/multiversx_sdk_cli/tests/test_cli_default_wallet.py index 0f7b5b1e..04b6765c 100644 --- a/multiversx_sdk_cli/tests/test_cli_default_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_default_wallet.py @@ -22,6 +22,10 @@ def test_empty_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", "--gas-limit", "50000", + "--nonce", + "0", + "--chain", + "D", ] ) out = _read_stdout(capsys) @@ -41,6 +45,10 @@ def test_empty_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): "50000", "--sender", "invalidSender", + "--nonce", + "0", + "--chain", + "D", ] ) assert return_code @@ -65,6 +73,10 @@ def test_without_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", "--gas-limit", "50000", + "--nonce", + "0", + "--chain", + "D", ] ) assert return_code @@ -84,6 +96,10 @@ def test_without_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): "50000", "--sender", "invalidAlias", + "--nonce", + "0", + "--chain", + "D", ] ) assert return_code @@ -117,6 +133,10 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", "--gas-limit", "50000", + "--nonce", + "0", + "--chain", + "D", ] ) assert return_code @@ -136,6 +156,10 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path "50000", "--sender", "alice", + "--nonce", + "0", + "--chain", + "D", ] ) assert return_code @@ -167,6 +191,10 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", "--gas-limit", "50000", + "--nonce", + "0", + "--chain", + "D", ] ) assert return_code @@ -186,6 +214,10 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path "50000", "--sender", "alice", + "--nonce", + "0", + "--chain", + "D", ] ) assert return_code @@ -232,6 +264,10 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path "50000", "--sender", "alice", + "--nonce", + "0", + "--chain", + "D", ] ) assert return_code From dd2746b8fd63a773fed305af83c039d1a0e46298 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 16 Jun 2025 16:26:56 +0300 Subject: [PATCH 41/64] try to fix tests --- multiversx_sdk_cli/tests/test_cli_default_wallet.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/multiversx_sdk_cli/tests/test_cli_default_wallet.py b/multiversx_sdk_cli/tests/test_cli_default_wallet.py index 04b6765c..fccdcd3b 100644 --- a/multiversx_sdk_cli/tests/test_cli_default_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_default_wallet.py @@ -12,6 +12,7 @@ def test_empty_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): import multiversx_sdk_cli.address_config monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + monkeypatch.setattr(multiversx_sdk_cli.address_config, "GLOBAL_ADDRESS_CONFIG_PATH", test_file) multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() return_code = main( @@ -63,6 +64,7 @@ def test_without_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): import multiversx_sdk_cli.address_config monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + monkeypatch.setattr(multiversx_sdk_cli.address_config, "GLOBAL_ADDRESS_CONFIG_PATH", test_file) multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() return_code = main( @@ -123,6 +125,7 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path import multiversx_sdk_cli.address_config monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + monkeypatch.setattr(multiversx_sdk_cli.address_config, "GLOBAL_ADDRESS_CONFIG_PATH", test_file) multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() return_code = main( @@ -181,6 +184,7 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path test_file.write_text(json.dumps(json_file)) monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) + monkeypatch.setattr(multiversx_sdk_cli.address_config, "GLOBAL_ADDRESS_CONFIG_PATH", test_file) multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() return_code = main( From 687cf4a1068d30a10f069fd7ed488b378a1594da Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 16 Jun 2025 16:36:57 +0300 Subject: [PATCH 42/64] fix test --- multiversx_sdk_cli/tests/test_cli_default_wallet.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk_cli/tests/test_cli_default_wallet.py b/multiversx_sdk_cli/tests/test_cli_default_wallet.py index fccdcd3b..883c1d40 100644 --- a/multiversx_sdk_cli/tests/test_cli_default_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_default_wallet.py @@ -58,7 +58,8 @@ def test_empty_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): def test_without_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): - test_file = tmp_path / "addresses.mxpy.json" + # Ensure the address config file does not exist; if the actual name is used, when running the tests locally, it will fail with a different error message + test_file = tmp_path / "test-addresses.mxpy.json" assert not test_file.exists() import multiversx_sdk_cli.address_config @@ -106,7 +107,7 @@ def test_without_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): ) assert return_code out = _read_stdout(capsys) - assert "Alias is not known: invalidAlias." in out + assert "The address config file was not found." in out def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): From 335ea7353edd135d901a299c7fabb056bfed1aa1 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Jun 2025 13:07:21 +0300 Subject: [PATCH 43/64] deprecate password file --- multiversx_sdk_cli/address_config.py | 30 ++++----- multiversx_sdk_cli/cli_password.py | 12 ++++ multiversx_sdk_cli/cli_shared.py | 67 ++++++++++--------- multiversx_sdk_cli/errors.py | 5 ++ .../tests/test_cli_default_wallet.py | 7 ++ 5 files changed, 73 insertions(+), 48 deletions(-) diff --git a/multiversx_sdk_cli/address_config.py b/multiversx_sdk_cli/address_config.py index 1920348b..f1bf731f 100644 --- a/multiversx_sdk_cli/address_config.py +++ b/multiversx_sdk_cli/address_config.py @@ -16,33 +16,33 @@ def get_defaults() -> dict[str, str]: - """Not all values are required for a config to be valid. - - Valid config for PEM wallets: + """ + Not all values are required for a config to be valid. + Valid config for PEM wallets: + ``` { - "kind": "pem", - "path": "/path/to/wallet.pem", - "index": "0", # optional, defaults to 0 + "kind": "pem", + "path": "/path/to/wallet.pem", + "index": "0" # optional, defaults to 0 } + ``` - Valid config for KEYSTORE wallets: - + Valid config for KEYSTORE wallets: + ``` { - "kind": "keystore", - "path": "/path/to/wallet.json", - "password": "somePassword", # if not set, passwordPath must be set - "passwordPath": "/path/to/password.txt", # if not set, password must be set - "index": "0", # optional, defaults to 0 + "kind": "keystore", + "path": "/path/to/wallet.json", + "index": "0" # optional, defaults to 0 } + ``` + For keystore wallets, you'll be prompted to enter the password when using the wallet. """ return { "kind": "", "path": "", "index": "", - "password": "", - "passwordPath": "", } diff --git a/multiversx_sdk_cli/cli_password.py b/multiversx_sdk_cli/cli_password.py index 98666c31..71a93c72 100644 --- a/multiversx_sdk_cli/cli_password.py +++ b/multiversx_sdk_cli/cli_password.py @@ -1,9 +1,15 @@ +import logging from getpass import getpass from typing import Any +logger = logging.getLogger("cli.password") + def load_password(args: Any) -> str: if args.passfile: + logger.warning( + "Using a password file is deprecated and will be removed in a future version. You'll be prompted to enter the password when using keystore wallets." + ) with open(args.passfile) as pass_file: return pass_file.read().strip() return getpass("Keyfile's password: ") @@ -11,6 +17,9 @@ def load_password(args: Any) -> str: def load_guardian_password(args: Any) -> str: if args.guardian_passfile: + logger.warning( + "Using a password file is deprecated and will be removed in a future version. You'll be prompted to enter the password when using keystore wallets." + ) with open(args.guardian_passfile) as pass_file: return pass_file.read().strip() return getpass("Keyfile's password: ") @@ -18,6 +27,9 @@ def load_guardian_password(args: Any) -> str: def load_relayer_password(args: Any) -> str: if args.relayer_passfile: + logger.warning( + "Using a password file is deprecated and will be removed in a future version. You'll be prompted to enter the password when using keystore wallets." + ) with open(args.relayer_passfile) as pass_file: return pass_file.read().strip() return getpass("Keyfile's password: ") diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 5be5ec67..9fefeb60 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -5,6 +5,7 @@ import sys from argparse import FileType from functools import cache +from getpass import getpass from pathlib import Path from typing import Any, Optional, Text, Union, cast @@ -47,6 +48,7 @@ LedgerError, NoWalletProvided, UnknownAddressAliasError, + WalletError, ) from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount @@ -181,7 +183,7 @@ def add_wallet_args(args: list[str], sub: Any): ) sub.add_argument( "--passfile", - help="🔑 a file containing keyfile's password, if keyfile provided. If not provided, you'll be prompted to enter the password.", + help="🔑 a file containing keyfile's password, if keyfile provided. This argument is DEPRECATED, you'll be prompted to enter the password.", ) sub.add_argument( "--ledger", @@ -225,7 +227,7 @@ def add_guardian_wallet_args(args: list[str], sub: Any): ) sub.add_argument( "--guardian-passfile", - help="🔑 a file containing keyfile's password, if keyfile provided. If not provided, you'll be prompted to enter the password.", + help="🔑 a file containing keyfile's password, if keyfile provided. This argument is DEPRECATED, you'll be prompted to enter the password.", ) sub.add_argument( "--guardian-ledger", @@ -246,7 +248,7 @@ def add_relayed_v3_wallet_args(args: list[str], sub: Any): sub.add_argument("--relayer-keyfile", help="🔑 a JSON keyfile, if PEM not provided") sub.add_argument( "--relayer-passfile", - help="🔑 a file containing keyfile's password, if keyfile provided. If not provided, you'll be prompted to enter the password.", + help="🔑 a file containing keyfile's password, if keyfile provided. This argument is DEPRECATED, you'll be prompted to enter the password.", ) sub.add_argument( "--relayer-ledger", @@ -357,12 +359,10 @@ def prepare_account(args: Any): password = load_password(args) index = args.sender_wallet_index if args.sender_wallet_index != 0 else None - return Account.new_from_keystore( - file_path=Path(args.keyfile), - password=password, - address_index=index, - hrp=hrp, - ) + try: + return Account.new_from_keystore(Path(args.keyfile), password=password, address_index=index, hrp=hrp) + except Exception as e: + raise WalletError(str(e)) elif args.ledger: try: return LedgerAccount(address_index=args.sender_wallet_index) @@ -414,18 +414,13 @@ def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Accoun if kind == "pem": return Account.new_from_pem(file_path=path, index=index, hrp=hrp) else: - password = wallet.get("password", "") - password_path = wallet.get("passwordPath", None) - - if not password and not password_path: - raise AddressConfigFileError( - "'password' or 'passwordPath' must be set in the address config for keystore wallets." - ) - - if password_path: - password = Path(password_path).read_text().splitlines()[0].strip() - - return Account.new_from_keystore(file_path=path, password=password, address_index=index, hrp=hrp) + logger.info("Using keystore wallet.") + password = getpass("Please enter the wallet password: ") + logger.info(f"Loading keystore wallet from path: {path}") + try: + return Account.new_from_keystore(file_path=path, password=password, address_index=index, hrp=hrp) + except Exception as e: + raise WalletError(str(e)) def _get_address_hrp(args: Any) -> str: @@ -472,12 +467,15 @@ def load_guardian_account(args: Any) -> Union[IAccount, None]: password = load_guardian_password(args) index = args.guardian_wallet_index if args.guardian_wallet_index != 0 else None - return Account.new_from_keystore( - file_path=Path(args.guardian_keyfile), - password=password, - address_index=index, - hrp=hrp, - ) + try: + return Account.new_from_keystore( + Path(args.guardian_keyfile), + password=password, + address_index=index, + hrp=hrp, + ) + except Exception as e: + raise WalletError(str(e)) elif args.guardian_ledger: try: return LedgerAccount(address_index=args.guardian_wallet_index) @@ -606,12 +604,15 @@ def load_relayer_account(args: Any) -> Union[IAccount, None]: password = load_relayer_password(args) index = args.relayer_wallet_index if args.relayer_wallet_index != 0 else None - return Account.new_from_keystore( - file_path=Path(args.relayer_keyfile), - password=password, - address_index=index, - hrp=hrp, - ) + try: + return Account.new_from_keystore( + Path(args.relayer_keyfile), + password=password, + address_index=index, + hrp=hrp, + ) + except Exception as e: + raise WalletError(str(e)) elif args.relayer_ledger: try: return LedgerAccount(address_index=args.relayer_wallet_index) diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index a7853764..377fa481 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -211,3 +211,8 @@ def __init__(self, message: str): class AddressConfigFileError(KnownError): def __init__(self, message: str): super().__init__(message) + + +class WalletError(KnownError): + def __init__(self, message: str): + super().__init__(message) diff --git a/multiversx_sdk_cli/tests/test_cli_default_wallet.py b/multiversx_sdk_cli/tests/test_cli_default_wallet.py index 883c1d40..d9631123 100644 --- a/multiversx_sdk_cli/tests/test_cli_default_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_default_wallet.py @@ -2,6 +2,7 @@ from pathlib import Path from typing import Any +from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.cli import main @@ -247,6 +248,8 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() + monkeypatch.setattr(cli_shared, "getpass", lambda *args, **kwargs: "") + return_code = main( [ "tx", @@ -255,6 +258,10 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", "--gas-limit", "50000", + "--nonce", + "0", + "--chain", + "D", ] ) assert return_code From fc6ed6f3d722f86fefec8a5d324ae8b3fde50df3 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Jun 2025 13:30:51 +0300 Subject: [PATCH 44/64] update to latest sdk-py --- pyproject.toml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 859ea6ba..273aac9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ dependencies = [ "ledgercomm[hid]", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk[ledger]==1.6.0" + "multiversx-sdk[ledger]==1.6.1" ] [project.scripts] diff --git a/requirements.txt b/requirements.txt index 3db43681..3bd9b9e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,4 @@ ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk[ledger]==1.6.0 +multiversx-sdk[ledger]==1.6.1 From 9d6ea299c9b3bb76929718f4ab320a495c22bfbf Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Jun 2025 13:34:53 +0300 Subject: [PATCH 45/64] fix test --- multiversx_sdk_cli/tests/test_cli_multisig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/tests/test_cli_multisig.py b/multiversx_sdk_cli/tests/test_cli_multisig.py index f2788f43..8af13e59 100644 --- a/multiversx_sdk_cli/tests/test_cli_multisig.py +++ b/multiversx_sdk_cli/tests/test_cli_multisig.py @@ -503,7 +503,7 @@ def test_transfer_and_execute_esdt(capsys: Any): data = base64.b64decode(data).decode() assert ( data - == "proposeTransferExecuteEsdt@0000000000000000050049bff963bdfa3ea02713362095df32e3d708eaccfc57@0000000c414c4943452d3536323766310000000000000000000000010a@0100000000004c4b40@3634363937333734373236393632373537343635" + == "proposeTransferExecuteEsdt@0000000000000000050049bff963bdfa3ea02713362095df32e3d708eaccfc57@0000000c414c4943452d3536323766310000000000000000000000010a@0100000000004c4b40@64697374726962757465" ) From 7253269f1e38c4ba4e29ecb6c498e9f8a2465be5 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 17 Jun 2025 13:48:06 +0300 Subject: [PATCH 46/64] adjust logger messages --- multiversx_sdk_cli/cli_password.py | 6 +++--- multiversx_sdk_cli/cli_shared.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/multiversx_sdk_cli/cli_password.py b/multiversx_sdk_cli/cli_password.py index 71a93c72..80535216 100644 --- a/multiversx_sdk_cli/cli_password.py +++ b/multiversx_sdk_cli/cli_password.py @@ -8,7 +8,7 @@ def load_password(args: Any) -> str: if args.passfile: logger.warning( - "Using a password file is deprecated and will be removed in a future version. You'll be prompted to enter the password when using keystore wallets." + "Using a password file is DEPRECATED and will be removed in a future version. Instead, you'll be prompted to enter the password when using keystore wallets." ) with open(args.passfile) as pass_file: return pass_file.read().strip() @@ -18,7 +18,7 @@ def load_password(args: Any) -> str: def load_guardian_password(args: Any) -> str: if args.guardian_passfile: logger.warning( - "Using a password file is deprecated and will be removed in a future version. You'll be prompted to enter the password when using keystore wallets." + "Using a password file is DEPRECATED and will be removed in a future version. Instead, you'll be prompted to enter the password when using keystore wallets." ) with open(args.guardian_passfile) as pass_file: return pass_file.read().strip() @@ -28,7 +28,7 @@ def load_guardian_password(args: Any) -> str: def load_relayer_password(args: Any) -> str: if args.relayer_passfile: logger.warning( - "Using a password file is deprecated and will be removed in a future version. You'll be prompted to enter the password when using keystore wallets." + "Using a password file is DEPRECATED and will be removed in a future version. Instead, you'll be prompted to enter the password when using keystore wallets." ) with open(args.relayer_passfile) as pass_file: return pass_file.read().strip() diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 9fefeb60..20dcc235 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -183,7 +183,7 @@ def add_wallet_args(args: list[str], sub: Any): ) sub.add_argument( "--passfile", - help="🔑 a file containing keyfile's password, if keyfile provided. This argument is DEPRECATED, you'll be prompted to enter the password.", + help="DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter the password.", ) sub.add_argument( "--ledger", @@ -227,7 +227,7 @@ def add_guardian_wallet_args(args: list[str], sub: Any): ) sub.add_argument( "--guardian-passfile", - help="🔑 a file containing keyfile's password, if keyfile provided. This argument is DEPRECATED, you'll be prompted to enter the password.", + help="DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter the password.", ) sub.add_argument( "--guardian-ledger", @@ -248,7 +248,7 @@ def add_relayed_v3_wallet_args(args: list[str], sub: Any): sub.add_argument("--relayer-keyfile", help="🔑 a JSON keyfile, if PEM not provided") sub.add_argument( "--relayer-passfile", - help="🔑 a file containing keyfile's password, if keyfile provided. This argument is DEPRECATED, you'll be prompted to enter the password.", + help="DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter the password.", ) sub.add_argument( "--relayer-ledger", From 4e06bbb3e421a79efd9050aa6fb7cfcbf49e23d6 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 18 Jun 2025 14:26:37 +0300 Subject: [PATCH 47/64] rename command --- CLI.md.sh | 2 +- multiversx_sdk_cli/cli_get.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CLI.md.sh b/CLI.md.sh index 84266426..670bfca4 100755 --- a/CLI.md.sh +++ b/CLI.md.sh @@ -207,7 +207,7 @@ generate() { group "Get" "get" command "Get.Account" "get account" - command "Get.Keys" "get keys" + command "Get.Storage" "get storage" command "Get.StorageEntry" "get storage-entry" command "Get.Token" "get token" command "Get.Transaction" "get transaction" diff --git a/multiversx_sdk_cli/cli_get.py b/multiversx_sdk_cli/cli_get.py index af335d65..8982d641 100644 --- a/multiversx_sdk_cli/cli_get.py +++ b/multiversx_sdk_cli/cli_get.py @@ -36,12 +36,12 @@ def setup_parser(subparsers: Any) -> Any: sub.set_defaults(func=get_account) sub = cli_shared.add_command_subparser( - subparsers, "get", "keys", "Get the storage (key-value pairs) of an account." + subparsers, "get", "storage", "Get the storage (key-value pairs) of an account." ) _add_alias_arg(sub) _add_address_arg(sub) _add_proxy_arg(sub) - sub.set_defaults(func=get_keys) + sub.set_defaults(func=get_storage) sub = cli_shared.add_command_subparser( subparsers, "get", "storage-entry", "Get a specific storage entry (key-value pair) of an account." @@ -121,7 +121,7 @@ def get_account(args: Any): dump_out_json(response.raw) -def get_keys(args: Any): +def get_storage(args: Any): if args.alias and args.address: raise BadUsage("Provide either '--alias' or '--address'") From a42793efc0ee77908cfc397c150d9e18a8aa653a Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 18 Jun 2025 16:03:03 +0300 Subject: [PATCH 48/64] change log message --- multiversx_sdk_cli/cli_shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 8af248a5..c7046f22 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -416,9 +416,9 @@ def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Accoun if kind == "pem": return Account.new_from_pem(file_path=path, index=index, hrp=hrp) else: - logger.info("Using keystore wallet.") + logger.info(f"Using keystore wallet at: [{path}].") password = getpass("Please enter the wallet password: ") - logger.info(f"Loading keystore wallet from path: {path}") + try: return Account.new_from_keystore(file_path=path, password=password, address_index=index, hrp=hrp) except Exception as e: From df27c87b48548ef5969236fd432f6d60c79f8da0 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 19 Jun 2025 10:48:50 +0300 Subject: [PATCH 49/64] fixes after review --- multiversx_sdk_cli/cli.py | 27 ++++++++++++++------------- multiversx_sdk_cli/config.py | 11 +++++++---- multiversx_sdk_cli/constants.py | 2 ++ multiversx_sdk_cli/errors.py | 5 ----- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 54f5f464..16acbb32 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -30,7 +30,7 @@ import multiversx_sdk_cli.version from multiversx_sdk_cli import config, errors, utils, ux from multiversx_sdk_cli.cli_shared import set_proxy_from_config_if_not_provided -from multiversx_sdk_cli.constants import SDK_PATH +from multiversx_sdk_cli.constants import LOG_LEVELS, SDK_PATH from multiversx_sdk_cli.env import get_address_hrp logger = logging.getLogger("cli") @@ -54,8 +54,7 @@ def _do_main(cli_args: list[str]): parser = setup_parser(cli_args) argcomplete.autocomplete(parser) - _handle_log_level_argument(cli_args) - _handle_verbose_argument(cli_args) + _handle_global_arguments(cli_args) args = parser.parse_args(cli_args) if args.verbose: @@ -118,7 +117,7 @@ def setup_parser(args: list[str]): "--log-level", type=str, default=config.get_log_level_from_config(), - choices=["debug", "info", "warning", "error"], + choices=LOG_LEVELS, help="default: %(default)s", ) @@ -168,23 +167,25 @@ def verify_deprecated_entries_in_config_file(): ux.show_warning(message.rstrip("\n")) -def _handle_verbose_argument(args: list[str]): - verbose_arg = "--verbose" - if verbose_arg in args: - args.remove(verbose_arg) - args.insert(0, verbose_arg) - - -def _handle_log_level_argument(args: list[str]): +def _handle_global_arguments(args: list[str]): + """ + Handle global arguments like --verbose and --log-level. + """ log_level_arg = "--log-level" - if log_level_arg in args: index = args.index(log_level_arg) + if index + 1 >= len(args): + raise ValueError(f"Argument {log_level_arg} must be followed by a log level value.") + log_arg = args.pop(index) log_value = args.pop(index) args.insert(0, log_value) args.insert(0, log_arg) + if "--verbose" in args: + args.remove("--verbose") + args.insert(0, "--verbose") + if __name__ == "__main__": ret = main(sys.argv[1:]) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 28022d7f..1044d465 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -6,7 +6,8 @@ from multiversx_sdk import NetworkProviderConfig from multiversx_sdk_cli import errors, utils -from multiversx_sdk_cli.constants import SDK_PATH +from multiversx_sdk_cli.constants import LOG_LEVELS, SDK_PATH +from multiversx_sdk_cli.ux import show_warning LOCAL_CONFIG_PATH = Path("mxpy.json").resolve() GLOBAL_CONFIG_PATH = SDK_PATH / "mxpy.json" @@ -140,10 +141,12 @@ def get_defaults() -> dict[str, Any]: } -def get_log_level_from_config(): +def get_log_level_from_config() -> str: log_level = get_value("log_level") - if log_level not in ["debug", "info", "warning", "error"]: - raise errors.LogLevelError(log_level) + if log_level not in LOG_LEVELS: + default_log_level = get_defaults()["log_level"] + show_warning(f"Invalid log level set in config: [{log_level}]. Defaulting to [{default_log_level}].") + return default_log_level return log_level diff --git a/multiversx_sdk_cli/constants.py b/multiversx_sdk_cli/constants.py index 26129b8f..803889fe 100644 --- a/multiversx_sdk_cli/constants.py +++ b/multiversx_sdk_cli/constants.py @@ -30,3 +30,5 @@ MAINCHAIN_ADDRESS_HRP = "erd" SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() + +LOG_LEVELS = ["debug", "info", "warning", "error"] diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index ff58a7f4..72275099 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -221,8 +221,3 @@ def __init__(self, message: str): class NetworkProviderError(KnownError): def __init__(self, url: str, error: str): super().__init__(f"Url = [{url}], error = {error}") - - -class LogLevelError(KnownError): - def __init__(self, log_level: str): - super().__init__(f"Log level not accepted: {log_level}. Choose between ['debug', 'info', 'warning', 'error'].") From a5f7c3b4198575f2478e0063f82eb16374c28a74 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 19 Jun 2025 12:45:04 +0300 Subject: [PATCH 50/64] fix mypy warning --- multiversx_sdk_cli/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 1044d465..9b6c89b5 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -144,7 +144,7 @@ def get_defaults() -> dict[str, Any]: def get_log_level_from_config() -> str: log_level = get_value("log_level") if log_level not in LOG_LEVELS: - default_log_level = get_defaults()["log_level"] + default_log_level: str = get_defaults()["log_level"] show_warning(f"Invalid log level set in config: [{log_level}]. Defaulting to [{default_log_level}].") return default_log_level From 38016fec1c80da3ad96649e722d539e605f91fd7 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 19 Jun 2025 13:30:16 +0300 Subject: [PATCH 51/64] update go version --- multiversx_sdk_cli/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 9b6c89b5..d8bdd00c 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -128,7 +128,7 @@ def _guard_valid_config_deletion(name: str): def get_defaults() -> dict[str, Any]: return { "dependencies.golang.resolution": "SDK", - "dependencies.golang.tag": "go1.20.7", + "dependencies.golang.tag": "go1.23.10", "dependencies.golang.urlTemplate.linux": "https://golang.org/dl/{TAG}.linux-amd64.tar.gz", "dependencies.golang.urlTemplate.osx": "https://golang.org/dl/{TAG}.darwin-amd64.tar.gz", "dependencies.golang.urlTemplate.windows": "https://golang.org/dl/{TAG}.windows-amd64.zip", From 10e852fd96950f0a73212f15ff51692f24996f85 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 19 Jun 2025 13:49:52 +0300 Subject: [PATCH 52/64] generate CLI.md file --- CLI.md | 1057 +++++++++++----------------- CLI.md.sh | 1 + multiversx_sdk_cli/cli_multisig.py | 36 +- 3 files changed, 445 insertions(+), 649 deletions(-) diff --git a/CLI.md b/CLI.md index d7caf3e9..7c572ddd 100644 --- a/CLI.md +++ b/CLI.md @@ -29,6 +29,8 @@ TOP-LEVEL OPTIONS: -h, --help show this help message and exit -v, --version show program's version number and exit --verbose + --log-level {debug,info,warning,error} + default: debug ---------------------- COMMAND GROUPS summary @@ -130,8 +132,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -162,15 +164,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -223,8 +225,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -260,15 +262,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -326,8 +328,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -358,15 +360,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -417,8 +419,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter the + password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -426,6 +428,34 @@ options: --hrp HRP The hrp used to convert the address to its bech32 representation --skip-confirmation, -y can be used to skip the confirmation prompt +``` +### Contract.Unverify + + +``` +$ mxpy contract unverify --help +usage: mxpy contract unverify [-h] ... + +Unverify a previously verified Smart Contract + +positional arguments: + contract 🖄 the bech32 address of the Smart Contract + +options: + -h, --help show this help message and exit + --code-hash CODE_HASH the code hash of the contract + --verifier-url VERIFIER_URL the url of the service that validates the contract + --sender SENDER the alias of the wallet set in the address config + --pem PEM 🔑 the PEM file, if keyfile not provided + --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter the + password. + --ledger 🔐 bool flag for signing transaction using ledger + --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic + or Ledger devices (default: 0) + --sender-username SENDER_USERNAME 🖄 the username of the sender + --hrp HRP The hrp used to convert the address to its bech32 representation + ``` ### Contract.ReproducibleBuild @@ -508,8 +538,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -540,15 +570,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -614,8 +644,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -630,15 +660,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -670,8 +700,8 @@ options: -h, --help show this help message and exit --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -730,8 +760,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -754,15 +784,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -786,8 +816,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -810,15 +840,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -840,8 +870,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -864,15 +894,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -894,8 +924,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -918,15 +948,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -948,8 +978,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -972,15 +1002,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1002,8 +1032,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1026,15 +1056,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1055,8 +1085,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1079,15 +1109,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1109,8 +1139,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1133,15 +1163,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1163,8 +1193,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1187,15 +1217,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1217,8 +1247,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1241,15 +1271,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1271,8 +1301,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1295,15 +1325,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1324,8 +1354,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1348,15 +1378,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1417,8 +1447,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1441,15 +1471,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1489,8 +1519,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1513,15 +1543,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1545,8 +1575,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1569,15 +1599,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1601,8 +1631,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1625,15 +1655,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1657,8 +1687,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1681,15 +1711,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1713,8 +1743,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1737,15 +1767,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1769,8 +1799,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1793,15 +1823,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1823,8 +1853,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1847,15 +1877,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1877,8 +1907,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1901,15 +1931,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1931,8 +1961,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1955,15 +1985,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -1985,8 +2015,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2009,15 +2039,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2039,8 +2069,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2063,15 +2093,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2094,8 +2124,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2118,15 +2148,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2149,8 +2179,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2173,15 +2203,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2205,8 +2235,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2229,15 +2259,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2261,8 +2291,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2285,15 +2315,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2318,8 +2348,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2342,15 +2372,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2375,8 +2405,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2399,15 +2429,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -2516,8 +2546,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter the + password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3038,8 +3068,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter the + password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3147,8 +3177,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3171,15 +3201,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3222,8 +3252,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3246,15 +3276,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3295,8 +3325,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3319,15 +3349,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3367,8 +3397,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3391,15 +3421,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3439,8 +3469,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3463,15 +3493,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3511,8 +3541,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3535,15 +3565,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3583,8 +3613,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3607,15 +3637,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3656,8 +3686,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3680,15 +3710,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3721,8 +3751,7 @@ Output example: options: -h, --help show this help message and exit - --opt-gas-limit OPT_GAS_LIMIT the size of the new quorum (number of signatures required to approve a - proposal) + --opt-gas-limit OPT_GAS_LIMIT optional gas limit for the async call --contract-abi CONTRACT_ABI the ABI file of the contract to call --function FUNCTION the function to call --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, @@ -3736,8 +3765,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3762,15 +3791,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3806,8 +3835,7 @@ options: --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 - --opt-gas-limit OPT_GAS_LIMIT the size of the new quorum (number of signatures required to approve a - proposal) + --opt-gas-limit OPT_GAS_LIMIT optional gas limit for the async call --contract-abi CONTRACT_ABI the ABI file of the contract to call --function FUNCTION the function to call --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, @@ -3821,8 +3849,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3847,15 +3875,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3892,8 +3920,7 @@ options: --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 - --opt-gas-limit OPT_GAS_LIMIT the size of the new quorum (number of signatures required to approve a - proposal) + --opt-gas-limit OPT_GAS_LIMIT optional gas limit for the async call --contract-abi CONTRACT_ABI the ABI file of the contract to call --function FUNCTION the function to call --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, @@ -3907,8 +3934,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3933,15 +3960,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -3992,8 +4019,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4016,15 +4043,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4075,8 +4102,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4099,15 +4126,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4147,8 +4174,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4171,15 +4198,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4219,8 +4246,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4243,15 +4270,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4291,8 +4318,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4315,15 +4342,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4363,8 +4390,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4387,15 +4414,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4435,8 +4462,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4459,15 +4486,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4507,8 +4534,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4531,15 +4558,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4581,8 +4608,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4605,15 +4632,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to + enter the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4654,8 +4681,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4678,15 +4705,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4726,8 +4753,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4750,15 +4777,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -4776,19 +4803,6 @@ usage: mxpy multisig get-quorum [-h] ... Perform a smart contract query to get the quorum. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -4805,19 +4819,6 @@ usage: mxpy multisig get-num-board-members [-h] ... Perform a smart contract query to get the number of board members. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -4834,19 +4835,6 @@ usage: mxpy multisig get-num-groups [-h] ... Perform a smart contract query to get the number of groups. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -4863,19 +4851,6 @@ usage: mxpy multisig get-num-proposers [-h] ... Perform a smart contract query to get the number of proposers. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -4892,19 +4867,6 @@ usage: mxpy multisig get-action-group [-h] ... Perform a smart contract query to get the actions in a group. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -4922,19 +4884,6 @@ usage: mxpy multisig get-last-action-group-id [-h] ... Perform a smart contract query to get the id of the last action in a group. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -4951,19 +4900,6 @@ usage: mxpy multisig get-action-last-index [-h] ... Perform a smart contract query to get the index of the last action. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -4980,19 +4916,6 @@ usage: mxpy multisig is-signed-by [-h] ... Perform a smart contract query to check if an action is signed by a user. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -5011,19 +4934,6 @@ usage: mxpy multisig is-quorum-reached [-h] ... Perform a smart contract query to check if an action has reached quorum. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -5041,19 +4951,6 @@ usage: mxpy multisig get-pending-actions [-h] ... Perform a smart contract query to get the pending actions full info. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -5070,19 +4967,6 @@ usage: mxpy multisig get-user-role [-h] ... Perform a smart contract query to get the role of a user. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -5100,19 +4984,6 @@ usage: mxpy multisig get-board-members [-h] ... Perform a smart contract query to get all the board members. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -5129,19 +5000,6 @@ usage: mxpy multisig get-proposers [-h] ... Perform a smart contract query to get all the proposers. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -5158,19 +5016,6 @@ usage: mxpy multisig get-action-data [-h] ... Perform a smart contract query to get the data of an action. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -5188,19 +5033,6 @@ usage: mxpy multisig get-action-signers [-h] ... Perform a smart contract query to get the signers of an action. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -5218,19 +5050,6 @@ usage: mxpy multisig get-action-signers-count [-h] ... Perform a smart contract query to get the number of signers of an action. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -5248,19 +5067,6 @@ usage: mxpy multisig get-action-valid-signers-count [-h] ... Perform a smart contract query to get the number of valid signers of an action. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --contract CONTRACT 🖄 the bech32 address of the Multisig Smart Contract @@ -5278,19 +5084,6 @@ usage: mxpy multisig parse-propose-action [-h] ... Parses the propose action transaction to extract proposal ID. -Output example: -=============== -{ - "emittedTransaction": { - "nonce": 42, - "sender": "alice", - "receiver": "bob", - "...": "..." - }, - "emittedTransactionData": "the transaction data, not encoded", - "emittedTransactionHash": "the transaction hash" -} - options: -h, --help show this help message and exit --abi ABI the ABI file of the Multisig Smart Contract @@ -5358,8 +5151,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5380,15 +5173,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5429,8 +5222,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5451,15 +5244,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5499,8 +5292,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5521,15 +5314,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5570,8 +5363,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5592,15 +5385,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5639,8 +5432,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5661,15 +5454,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5713,8 +5506,8 @@ options: --sender SENDER the alias of the wallet set in the address config --pem PEM 🔑 the PEM file, if keyfile not provided --keyfile KEYFILE 🔑 a JSON keyfile, if PEM not provided - --passfile PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --passfile PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --ledger 🔐 bool flag for signing transaction using ledger --sender-wallet-index SENDER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -5735,15 +5528,15 @@ options: --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian --guardian-pem GUARDIAN_PEM 🔑 the PEM file, if keyfile not provided --guardian-keyfile GUARDIAN_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --guardian-passfile GUARDIAN_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --guardian-passfile GUARDIAN_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --guardian-ledger 🔐 bool flag for signing transaction using ledger --guardian-wallet-index GUARDIAN_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided - --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided. If not - provided, you'll be prompted to enter the password. + --relayer-passfile RELAYER_PASSFILE DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter + the password. --relayer-ledger 🔐 bool flag for signing transaction using ledger --relayer-wallet-index RELAYER_WALLET_INDEX 🔑 the address index; can be used for PEM files, keyfiles of type mnemonic or Ledger devices (default: 0) @@ -6166,7 +5959,7 @@ usage: mxpy get COMMAND [-h] ... Get info from the network. COMMANDS: - {account,keys,storage-entry,token,transaction} + {account,storage,storage-entry,token,transaction,network-config,network-status} OPTIONS: -h, --help show this help message and exit @@ -6175,10 +5968,12 @@ OPTIONS: COMMANDS summary ---------------- account Get info about an account. -keys Get the storage (key-value pairs) of an account. +storage Get the storage (key-value pairs) of an account. storage-entry Get a specific storage entry (key-value pair) of an account. token Get a token of an account. transaction Get a transaction from the network. +network-config Get the network configuration. +network-status Get the network status. ``` ### Get.Account @@ -6198,12 +5993,12 @@ options: --balance whether to only fetch the balance of the address ``` -### Get.Keys +### Get.Storage ``` -$ mxpy get keys --help -usage: mxpy get keys [-h] ... +$ mxpy get storage --help +usage: mxpy get storage [-h] ... Get the storage (key-value pairs) of an account. diff --git a/CLI.md.sh b/CLI.md.sh index 670bfca4..2ad195f3 100755 --- a/CLI.md.sh +++ b/CLI.md.sh @@ -45,6 +45,7 @@ generate() { command "Contract.Upgrade" "contract upgrade" command "Contract.Query" "contract query" command "Contract.Verify" "contract verify" + command "Contract.Unverify" "contract unverify" command "Contract.ReproducibleBuild" "contract reproducible-build" group "Transactions" "tx" diff --git a/multiversx_sdk_cli/cli_multisig.py b/multiversx_sdk_cli/cli_multisig.py index efd3202a..149fce85 100644 --- a/multiversx_sdk_cli/cli_multisig.py +++ b/multiversx_sdk_cli/cli_multisig.py @@ -377,7 +377,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-quorum", - f"Perform a smart contract query to get the quorum.{output_description}", + "Perform a smart contract query to get the quorum.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -389,7 +389,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-num-board-members", - f"Perform a smart contract query to get the number of board members.{output_description}", + "Perform a smart contract query to get the number of board members.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -401,7 +401,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-num-groups", - f"Perform a smart contract query to get the number of groups.{output_description}", + "Perform a smart contract query to get the number of groups.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -413,7 +413,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-num-proposers", - f"Perform a smart contract query to get the number of proposers.{output_description}", + "Perform a smart contract query to get the number of proposers.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -425,7 +425,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-action-group", - f"Perform a smart contract query to get the actions in a group.{output_description}", + "Perform a smart contract query to get the actions in a group.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -438,7 +438,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-last-action-group-id", - f"Perform a smart contract query to get the id of the last action in a group.{output_description}", + "Perform a smart contract query to get the id of the last action in a group.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -450,7 +450,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-action-last-index", - f"Perform a smart contract query to get the index of the last action.{output_description}", + "Perform a smart contract query to get the index of the last action.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -462,7 +462,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "is-signed-by", - f"Perform a smart contract query to check if an action is signed by a user.{output_description}", + "Perform a smart contract query to check if an action is signed by a user.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -476,7 +476,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "is-quorum-reached", - f"Perform a smart contract query to check if an action has reached quorum.{output_description}", + "Perform a smart contract query to check if an action has reached quorum.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -489,7 +489,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-pending-actions", - f"Perform a smart contract query to get the pending actions full info.{output_description}", + "Perform a smart contract query to get the pending actions full info.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -501,7 +501,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-user-role", - f"Perform a smart contract query to get the role of a user.{output_description}", + "Perform a smart contract query to get the role of a user.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -514,7 +514,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-board-members", - f"Perform a smart contract query to get all the board members.{output_description}", + "Perform a smart contract query to get all the board members.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -526,7 +526,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-proposers", - f"Perform a smart contract query to get all the proposers.{output_description}", + "Perform a smart contract query to get all the proposers.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -538,7 +538,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-action-data", - f"Perform a smart contract query to get the data of an action.{output_description}", + "Perform a smart contract query to get the data of an action.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -551,7 +551,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-action-signers", - f"Perform a smart contract query to get the signers of an action.{output_description}", + "Perform a smart contract query to get the signers of an action.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -564,7 +564,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-action-signers-count", - f"Perform a smart contract query to get the number of signers of an action.{output_description}", + "Perform a smart contract query to get the number of signers of an action.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -577,7 +577,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "get-action-valid-signers-count", - f"Perform a smart contract query to get the number of valid signers of an action.{output_description}", + "Perform a smart contract query to get the number of valid signers of an action.", ) _add_contract_arg(sub) _add_abi_arg(sub) @@ -590,7 +590,7 @@ def setup_parser(args: list[str], subparsers: Any) -> Any: subparsers, "multisig", "parse-propose-action", - f"Parses the propose action transaction to extract proposal ID.{output_description}", + "Parses the propose action transaction to extract proposal ID.", ) _add_abi_arg(sub) sub.add_argument("--hash", required=True, type=str, help="the transaction hash of the propose action") From 07f4ee2ad61ae5eb09ea999810d9d1a114c307f6 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 19 Jun 2025 13:59:10 +0300 Subject: [PATCH 53/64] revert to previous go version --- multiversx_sdk_cli/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index d8bdd00c..9b6c89b5 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -128,7 +128,7 @@ def _guard_valid_config_deletion(name: str): def get_defaults() -> dict[str, Any]: return { "dependencies.golang.resolution": "SDK", - "dependencies.golang.tag": "go1.23.10", + "dependencies.golang.tag": "go1.20.7", "dependencies.golang.urlTemplate.linux": "https://golang.org/dl/{TAG}.linux-amd64.tar.gz", "dependencies.golang.urlTemplate.osx": "https://golang.org/dl/{TAG}.darwin-amd64.tar.gz", "dependencies.golang.urlTemplate.windows": "https://golang.org/dl/{TAG}.windows-amd64.zip", From f3cd8f260de24dddfb31c6e498fdea669ace7b8d Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 23 Jun 2025 10:49:47 +0300 Subject: [PATCH 54/64] bump version --- pyproject.toml | 4 ++-- requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 273aac9b..69697e62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "multiversx-sdk-cli" -version = "10.2.0" +version = "11.0.0" authors = [ { name="MultiversX" }, ] @@ -25,7 +25,7 @@ dependencies = [ "ledgercomm[hid]", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk[ledger]==1.6.1" + "multiversx-sdk[ledger]==1.6.2" ] [project.scripts] diff --git a/requirements.txt b/requirements.txt index 3bd9b9e5..ac022b32 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,4 @@ ledgercomm[hid] rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk[ledger]==1.6.1 +multiversx-sdk[ledger]==1.6.2 From 2888f7a0aa45f066c1cfd35e16fddf21e1124e3b Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 24 Jun 2025 16:48:55 +0300 Subject: [PATCH 55/64] refactoring config-wallet --- CLI.md | 130 ++++++------ CLI.md.sh | 20 +- multiversx_sdk_cli/address_config.py | 163 --------------- multiversx_sdk_cli/cli.py | 4 +- .../{cli_address.py => cli_config_wallet.py} | 86 ++++---- multiversx_sdk_cli/cli_shared.py | 56 +++-- multiversx_sdk_cli/config_wallet.py | 192 ++++++++++++++++++ multiversx_sdk_cli/errors.py | 2 +- .../tests/test_cli_default_wallet.py | 108 +++------- 9 files changed, 367 insertions(+), 394 deletions(-) delete mode 100644 multiversx_sdk_cli/address_config.py rename multiversx_sdk_cli/{cli_address.py => cli_config_wallet.py} (51%) create mode 100644 multiversx_sdk_cli/config_wallet.py diff --git a/CLI.md b/CLI.md index 7c572ddd..52c1fa51 100644 --- a/CLI.md +++ b/CLI.md @@ -23,7 +23,7 @@ See: COMMAND GROUPS: - {address,contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet,multisig,governance,env,get} + {config-wallet,contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet,multisig,governance,env,get} TOP-LEVEL OPTIONS: -h, --help show this help message and exit @@ -35,7 +35,7 @@ TOP-LEVEL OPTIONS: ---------------------- COMMAND GROUPS summary ---------------------- -address Configure MultiversX CLI to use a default wallet. +config-wallet Configure MultiversX CLI to use a default wallet. contract Deploy, upgrade and interact with Smart Contracts tx Create and broadcast Transactions validator Stake, UnStake, UnBond, Unjail and other actions useful for Validators @@ -5783,12 +5783,12 @@ options: -h, --help show this help message and exit ``` -## Group **Address** +## Group **ConfigWallet** ``` -$ mxpy address --help -usage: mxpy address COMMAND [-h] ... +$ mxpy config-wallet --help +usage: mxpy config-wallet COMMAND [-h] ... Configure MultiversX CLI to use a default wallet. @@ -5801,149 +5801,149 @@ OPTIONS: ---------------- COMMANDS summary ---------------- -new Creates a new address config and sets it as the active address. -list List available addresses -dump Dumps the active address. -get Gets a config value from the active address. -set Sets a config value for the active address. -delete Deletes a config value from the active address. -switch Switch to a different address. -remove Deletes an address using the alias. No default address will be set. Use `address switch` to set a new address. -reset Deletes the config file. No default address will be set. +new Creates a new wallet config and sets it as the active wallet. +list List configured wallets +dump Dumps the active wallet. +get Gets a config value from the specified wallet. +set Sets a config value for the specified wallet. +delete Deletes a config value from the specified wallet. +switch Switch to a different wallet. +remove Removes a wallet from the config using the alias. No default wallet will be set. Use `config-wallet switch` to set a new wallet. +reset Deletes the config file. No default wallet will be set. ``` -### Address.New +### ConfigWallet.New ``` -$ mxpy address new --help -usage: mxpy address new [-h] ... +$ mxpy config-wallet new --help +usage: mxpy config-wallet new [-h] ... -Creates a new address config and sets it as the active address. +Creates a new wallet config and sets it as the active wallet. positional arguments: alias the alias of the wallet options: -h, --help show this help message and exit - --template TEMPLATE an address config from which to create the new address + --path PATH the absolute path to the wallet file + --template TEMPLATE a wallet config from which to create the new config ``` -### Address.List +### ConfigWallet.List ``` -$ mxpy address list --help -usage: mxpy address list [-h] ... +$ mxpy config-wallet list --help +usage: mxpy config-wallet list [-h] ... -List available addresses +List configured wallets options: -h, --help show this help message and exit ``` -### Address.Dump +### ConfigWallet.Dump ``` -$ mxpy address dump --help -usage: mxpy address dump [-h] ... +$ mxpy config-wallet dump --help +usage: mxpy config-wallet dump [-h] ... -Dumps the active address. +Dumps the active wallet. options: -h, --help show this help message and exit ``` -### Address.Get +### ConfigWallet.Get ``` -$ mxpy address get --help -usage: mxpy address get [-h] ... +$ mxpy config-wallet get --help +usage: mxpy config-wallet get [-h] ... -Gets a config value from the active address. +Gets a config value from the specified wallet. positional arguments: - value the value to get from the active address (e.g. path) + value the value to get from the specified wallet (e.g. path) options: - -h, --help show this help message and exit + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet ``` -### Address.Set +### ConfigWallet.Set ``` -$ mxpy address set --help -usage: mxpy address set [-h] ... +$ mxpy config-wallet set --help +usage: mxpy config-wallet set [-h] ... -Sets a config value for the active address. +Sets a config value for the specified wallet. positional arguments: - key the key to set for the active address (e.g. index) - value the value to set for the specified key + key the key to set for the specified wallet (e.g. index) + value the value to set for the specified key options: - -h, --help show this help message and exit + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet ``` -### Address.Set +### ConfigWallet.Set ``` -$ mxpy address delete --help -usage: mxpy address delete [-h] ... +$ mxpy config-wallet delete --help +usage: mxpy config-wallet delete [-h] ... -Deletes a config value from the active address. +Deletes a config value from the specified wallet. positional arguments: - value the value to delete for the active address + value the value to delete for the specified address options: - -h, --help show this help message and exit + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet ``` -### Address.Switch +### ConfigWallet.Switch ``` -$ mxpy address switch --help -usage: mxpy address switch [-h] ... - -Switch to a different address. +$ mxpy config-wallet switch --help +usage: mxpy config-wallet switch [-h] ... -positional arguments: - alias the alias of the wallet +Switch to a different wallet. options: - -h, --help show this help message and exit + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet ``` -### Address.Remove +### ConfigWallet.Remove ``` -$ mxpy address remove --help -usage: mxpy address remove [-h] ... +$ mxpy config-wallet remove --help +usage: mxpy config-wallet remove [-h] ... -Deletes an address using the alias. No default address will be set. Use `address switch` to set a new address. - -positional arguments: - alias the alias of the wallet +Removes a wallet from the config using the alias. No default wallet will be set. Use `config-wallet switch` to set a new wallet. options: - -h, --help show this help message and exit + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet ``` -### Address.Reset +### ConfigWallet.Reset ``` -$ mxpy address reset --help +$ mxpy config-wallet reset --help usage: mxpy address reset [-h] ... -Deletes the config file. No default address will be set. +Deletes the config file. No default wallet will be set. options: -h, --help show this help message and exit diff --git a/CLI.md.sh b/CLI.md.sh index 2ad195f3..f7b492bb 100755 --- a/CLI.md.sh +++ b/CLI.md.sh @@ -195,16 +195,16 @@ generate() { command "Environment.Remove" "env remove" command "Environment.Reset" "env reset" - group "Address" "address" - command "Address.New" "address new" - command "Address.List" "address list" - command "Address.Dump" "address dump" - command "Address.Get" "address get" - command "Address.Set" "address set" - command "Address.Set" "address delete" - command "Address.Switch" "address switch" - command "Address.Remove" "address remove" - command "Address.Reset" "address reset" + group "ConfigWallet" "config-wallet" + command "ConfigWallet.New" "config-wallet new" + command "ConfigWallet.List" "config-wallet list" + command "ConfigWallet.Dump" "config-wallet dump" + command "ConfigWallet.Get" "config-wallet get" + command "ConfigWallet.Set" "config-wallet set" + command "ConfigWallet.Set" "config-wallet delete" + command "ConfigWallet.Switch" "config-wallet switch" + command "ConfigWallet.Remove" "config-wallet remove" + command "ConfigWallet.Reset" "config-wallet reset" group "Get" "get" command "Get.Account" "get account" diff --git a/multiversx_sdk_cli/address_config.py b/multiversx_sdk_cli/address_config.py deleted file mode 100644 index f1bf731f..00000000 --- a/multiversx_sdk_cli/address_config.py +++ /dev/null @@ -1,163 +0,0 @@ -from functools import cache -from pathlib import Path -from typing import Any - -from multiversx_sdk_cli.constants import SDK_PATH -from multiversx_sdk_cli.errors import ( - AliasAlreadyExistsError, - AliasProtectedError, - InvalidAddressConfigValue, - UnknownAddressAliasError, -) -from multiversx_sdk_cli.utils import read_json_file, write_json_file - -LOCAL_ADDRESS_CONFIG_PATH = Path("addresses.mxpy.json").resolve() -GLOBAL_ADDRESS_CONFIG_PATH = SDK_PATH / "addresses.mxpy.json" - - -def get_defaults() -> dict[str, str]: - """ - Not all values are required for a config to be valid. - - Valid config for PEM wallets: - ``` - { - "kind": "pem", - "path": "/path/to/wallet.pem", - "index": "0" # optional, defaults to 0 - } - ``` - - Valid config for KEYSTORE wallets: - ``` - { - "kind": "keystore", - "path": "/path/to/wallet.json", - "index": "0" # optional, defaults to 0 - } - ``` - - For keystore wallets, you'll be prompted to enter the password when using the wallet. - """ - return { - "kind": "", - "path": "", - "index": "", - } - - -@cache -def get_value(name: str) -> str: - _guard_valid_name(name) - data = get_active_address() - default_value = get_defaults()[name] - value = data.get(name, default_value) - assert isinstance(value, str) - return value - - -def _guard_valid_name(name: str): - if name not in get_defaults().keys(): - raise InvalidAddressConfigValue(f"Key is not present in address config: [{name}]") - - -def get_active_address() -> dict[str, str]: - """Returns the active address configuration.""" - data = read_address_config_file() - addresses: dict[str, Any] = data.get("addresses", {}) - active_address: str = data.get("active", "default") - result: dict[str, str] = addresses.get(active_address, {}) - - return result - - -@cache -def read_address_config_file() -> dict[str, Any]: - config_path = resolve_address_config_path() - if config_path.exists(): - data: dict[str, Any] = read_json_file(config_path) - return data - return dict() - - -def resolve_address_config_path() -> Path: - if LOCAL_ADDRESS_CONFIG_PATH.is_file(): - return LOCAL_ADDRESS_CONFIG_PATH - return GLOBAL_ADDRESS_CONFIG_PATH - - -def set_value(name: str, value: Any): - """Sets a key-value pair in the active address config.""" - _guard_valid_name(name) - data = read_address_config_file() - active_env = data.get("active", "default") - data.setdefault("addresses", {}) - data["addresses"].setdefault(active_env, {}) - data["addresses"][active_env][name] = value - _write_file(data) - - -def _write_file(data: dict[str, Any]): - env_path = resolve_address_config_path() - write_json_file(str(env_path), data) - - -def set_active(name: str): - """Switches to the address configuration with the given name.""" - data = read_address_config_file() - _guard_valid_address_name(data, name) - data["active"] = name - _write_file(data) - - -def _guard_valid_address_name(env: Any, name: str): - envs = env.get("addresses", {}) - if name not in envs: - raise UnknownAddressAliasError(name) - - -def create_new_address_config(name: str, template: str): - """Creates a new address config with the given name and optional template.""" - data = read_address_config_file() - _guard_alias_unique(data, name) - new_address = {} - if template: - _guard_valid_address_name(data, template) - new_address = data["addresses"][template] - - data["active"] = name - data.setdefault("addresses", {}) - data["addresses"][name] = new_address - _write_file(data) - - -def _guard_alias_unique(env: Any, name: str): - envs = env.get("addresses", {}) - if name in envs: - raise AliasAlreadyExistsError(name) - - -def delete_config_value(name: str): - """Deletes a key-value pair of the active address config.""" - _guard_valid_alias_deletion(name) - data = read_address_config_file() - active_env = data.get("active", "default") - data.setdefault("addresses", {}) - data["addresses"].setdefault(active_env, {}) - del data["addresses"][active_env][name] - _write_file(data) - - -def delete_alias(name: str): - """Deletes the address configuration with the given name.""" - _guard_valid_alias_deletion(name) - data = read_address_config_file() - data["addresses"].pop(name, None) - if data["active"] == name: - data["active"] = "default" - _write_file(data) - - -def _guard_valid_alias_deletion(name: str): - if name == "default": - raise AliasProtectedError(name) diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 16acbb32..0e2a2639 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -9,8 +9,8 @@ from multiversx_sdk import LibraryConfig from rich.logging import RichHandler -import multiversx_sdk_cli.cli_address import multiversx_sdk_cli.cli_config +import multiversx_sdk_cli.cli_config_wallet import multiversx_sdk_cli.cli_contracts import multiversx_sdk_cli.cli_data import multiversx_sdk_cli.cli_delegation @@ -124,7 +124,7 @@ def setup_parser(args: list[str]): subparsers = parser.add_subparsers() commands: list[Any] = [] - commands.append(multiversx_sdk_cli.cli_address.setup_parser(subparsers)) + commands.append(multiversx_sdk_cli.cli_config_wallet.setup_parser(subparsers)) commands.append(multiversx_sdk_cli.cli_contracts.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_transactions.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_validators.setup_parser(args, subparsers)) diff --git a/multiversx_sdk_cli/cli_address.py b/multiversx_sdk_cli/cli_config_wallet.py similarity index 51% rename from multiversx_sdk_cli/cli_address.py rename to multiversx_sdk_cli/cli_config_wallet.py index 771f3a85..e1e2cb83 100644 --- a/multiversx_sdk_cli/cli_address.py +++ b/multiversx_sdk_cli/cli_config_wallet.py @@ -3,68 +3,78 @@ from typing import Any from multiversx_sdk_cli import cli_shared -from multiversx_sdk_cli.address_config import ( - create_new_address_config, +from multiversx_sdk_cli.config_wallet import ( + create_new_wallet_config, delete_alias, delete_config_value, - get_active_address, + get_active_wallet, get_value, - read_address_config_file, - resolve_address_config_path, + read_wallet_config_file, + resolve_wallet_config_path, set_active, set_value, ) from multiversx_sdk_cli.utils import dump_out_json from multiversx_sdk_cli.ux import confirm_continuation -logger = logging.getLogger("cli.address") +logger = logging.getLogger("cli.config_wallet") def setup_parser(subparsers: Any) -> Any: - parser = cli_shared.add_group_subparser(subparsers, "address", "Configure MultiversX CLI to use a default wallet.") + parser = cli_shared.add_group_subparser( + subparsers, "config-wallet", "Configure MultiversX CLI to use a default wallet." + ) subparsers = parser.add_subparsers() sub = cli_shared.add_command_subparser( - subparsers, "address", "new", "Creates a new address config and sets it as the active address." + subparsers, "config-wallet", "new", "Creates a new wallet config and sets it as the active wallet." ) - _add_alias_arg(sub) + sub.add_argument("alias", type=str, help="the alias of the wallet") + sub.add_argument("--path", type=str, required=False, help="the absolute path to the wallet file") sub.add_argument( "--template", required=False, - help="an address config from which to create the new address", + help="a wallet config from which to create the new config", ) - sub.set_defaults(func=new_address_config) + sub.set_defaults(func=new_wallet_config) - sub = cli_shared.add_command_subparser(subparsers, "address", "list", "List available addresses") + sub = cli_shared.add_command_subparser(subparsers, "config-wallet", "list", "List configured wallets") sub.set_defaults(func=list_addresses) - sub = cli_shared.add_command_subparser(subparsers, "address", "dump", "Dumps the active address.") + sub = cli_shared.add_command_subparser(subparsers, "config-wallet", "dump", "Dumps the active wallet.") sub.set_defaults(func=dump) - sub = cli_shared.add_command_subparser(subparsers, "address", "get", "Gets a config value from the active address.") - sub.add_argument("value", type=str, help="the value to get from the active address (e.g. path)") + sub = cli_shared.add_command_subparser( + subparsers, "config-wallet", "get", "Gets a config value from the specified wallet." + ) + sub.add_argument("value", type=str, help="the value to get from the specified wallet (e.g. path)") + _add_alias_arg(sub) sub.set_defaults(func=get_address_config_value) - sub = cli_shared.add_command_subparser(subparsers, "address", "set", "Sets a config value for the active address.") - sub.add_argument("key", type=str, help="the key to set for the active address (e.g. index)") + sub = cli_shared.add_command_subparser( + subparsers, "config-wallet", "set", "Sets a config value for the specified wallet." + ) + sub.add_argument("key", type=str, help="the key to set for the specified wallet (e.g. index)") sub.add_argument("value", type=str, help="the value to set for the specified key") + _add_alias_arg(sub) sub.set_defaults(func=set_address_config_value) sub = cli_shared.add_command_subparser( - subparsers, "address", "delete", "Deletes a config value from the active address." + subparsers, "config-wallet", "delete", "Deletes a config value from the specified wallet." ) - sub.add_argument("value", type=str, help="the value to delete for the active address") + sub.add_argument("value", type=str, help="the value to delete for the specified address") + _add_alias_arg(sub) sub.set_defaults(func=delete_address_config_value) - sub = cli_shared.add_command_subparser(subparsers, "address", "switch", "Switch to a different address.") + sub = cli_shared.add_command_subparser(subparsers, "config-wallet", "switch", "Switch to a different wallet.") _add_alias_arg(sub) sub.set_defaults(func=switch_address) sub = cli_shared.add_command_subparser( subparsers, - "address", + "config-wallet", "remove", - "Deletes an address using the alias. No default address will be set. Use `address switch` to set a new address.", + "Removes a wallet from the config using the alias. No default wallet will be set. Use `config-wallet switch` to set a new wallet.", ) _add_alias_arg(sub) sub.set_defaults(func=remove_address) @@ -73,7 +83,7 @@ def setup_parser(subparsers: Any) -> Any: subparsers, "address", "reset", - "Deletes the config file. No default address will be set.", + "Deletes the config file. No default wallet will be set.", ) sub.set_defaults(func=delete_address_config_file) @@ -82,49 +92,49 @@ def setup_parser(subparsers: Any) -> Any: def _add_alias_arg(sub: Any): - sub.add_argument("alias", type=str, help="the alias of the wallet") + sub.add_argument("--alias", type=str, required=True, help="the alias of the wallet") -def new_address_config(args: Any): - create_new_address_config(name=args.alias, template=args.template) - dump_out_json(get_active_address()) +def new_wallet_config(args: Any): + create_new_wallet_config(name=args.alias, path=args.path, template=args.template) + dump_out_json(get_active_wallet()) def list_addresses(args: Any): _ensure_address_config_file_exists() - data = read_address_config_file() + data = read_wallet_config_file() dump_out_json(data) def dump(args: Any): _ensure_address_config_file_exists() - dump_out_json(get_active_address()) + dump_out_json(get_active_wallet()) def get_address_config_value(args: Any): _ensure_address_config_file_exists() - value = get_value(args.value) + value = get_value(args.value, args.alias) print(value) def set_address_config_value(args: Any): _ensure_address_config_file_exists() - set_value(args.key, args.value) + set_value(args.key, args.value, args.alias) def delete_address_config_value(args: Any): _ensure_address_config_file_exists() - delete_config_value(args.value) - dump_out_json(get_active_address()) + delete_config_value(args.value, args.alias) + dump_out_json(get_active_wallet()) def switch_address(args: Any): _ensure_address_config_file_exists() set_active(args.alias) - dump_out_json(get_active_address()) + dump_out_json(get_active_wallet()) def remove_address(args: Any): @@ -133,9 +143,9 @@ def remove_address(args: Any): def delete_address_config_file(args: Any): - address_file = resolve_address_config_path() + address_file = resolve_wallet_config_path() if not address_file.is_file(): - logger.info("Address config file not found. Aborting...") + logger.info("Wallet config file not found. Aborting...") return confirm_continuation(f"The file `{str(address_file)}` will be deleted. Do you want to continue? (y/n)") @@ -144,7 +154,7 @@ def delete_address_config_file(args: Any): def _ensure_address_config_file_exists(): - address_file = resolve_address_config_path() + address_file = resolve_wallet_config_path() if not address_file.is_file(): - logger.info("Address config file not found. Aborting...") + logger.info("Wallet config file not found. Aborting...") exit(1) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index c7046f22..b84a203e 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -22,17 +22,17 @@ ) from multiversx_sdk_cli import config, utils -from multiversx_sdk_cli.address_config import ( - get_active_address, - read_address_config_file, - resolve_address_config_path, -) from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.cli_password import ( load_guardian_password, load_password, load_relayer_password, ) +from multiversx_sdk_cli.config_wallet import ( + get_active_wallet, + read_wallet_config_file, + resolve_wallet_config_path, +) from multiversx_sdk_cli.constants import ( DEFAULT_GAS_PRICE, DEFAULT_TX_VERSION, @@ -44,10 +44,9 @@ ArgumentsNotProvidedError, BadUsage, IncorrectWalletError, - InvalidAddressConfigValue, LedgerError, NoWalletProvided, - UnknownAddressAliasError, + UnknownWalletAliasError, WalletError, ) from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData @@ -369,53 +368,46 @@ def prepare_account(args: Any): def load_wallet_by_alias(alias: str, hrp: str) -> Account: - file_path = resolve_address_config_path() + file_path = resolve_wallet_config_path() if not file_path.is_file(): - raise AddressConfigFileError("The address config file was not found.") + raise AddressConfigFileError("The wallet config file was not found.") - file = read_address_config_file() + file = read_wallet_config_file() if file == dict(): - raise AddressConfigFileError("Address config file is empty.") + raise AddressConfigFileError("Wallet config file is empty.") - addresses: dict[str, Any] = file["addresses"] - wallet = addresses.get(alias, None) + wallets: dict[str, Any] = file["wallets"] + wallet = wallets.get(alias, None) if not wallet: - raise UnknownAddressAliasError(alias) + raise UnknownWalletAliasError(alias) - logger.info(f"Using sender [{alias}] from address config.") + logger.info(f"Using sender [{alias}] from wallet config.") return _load_wallet_from_address_config(wallet=wallet, hrp=hrp) def load_default_wallet(hrp: str) -> Account: - active_address = get_active_address() - if active_address == dict(): - logger.info("No default wallet found in address config.") + active_wallet = get_active_wallet() + if active_wallet == dict(): + logger.info("No default wallet found in wallet config.") raise NoWalletProvided() - alias_of_default_wallet = read_address_config_file().get("active", "") - logger.info(f"Using sender [{alias_of_default_wallet}] from address config.") + alias_of_default_wallet = read_wallet_config_file().get("active", "") + logger.info(f"Using sender [{alias_of_default_wallet}] from wallet config.") - return _load_wallet_from_address_config(wallet=active_address, hrp=hrp) + return _load_wallet_from_address_config(wallet=active_wallet, hrp=hrp) def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Account: - kind = wallet.get("kind", None) - if not kind: - raise AddressConfigFileError("'kind' field must be set in the address config.") - - if kind not in ["pem", "keystore"]: - raise InvalidAddressConfigValue("'kind' must be 'pem' or 'keystore'") - wallet_path = wallet.get("path", None) if not wallet_path: - raise AddressConfigFileError("'path' field must be set in the address config.") + raise AddressConfigFileError("'path' field must be set in the wallet config.") path = Path(wallet_path) index = int(wallet.get("index", 0)) - if kind == "pem": + if path.suffix == ".pem": return Account.new_from_pem(file_path=path, index=index, hrp=hrp) - else: + elif path.suffix == ".json": logger.info(f"Using keystore wallet at: [{path}].") password = getpass("Please enter the wallet password: ") @@ -423,6 +415,8 @@ def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Accoun return Account.new_from_keystore(file_path=path, password=password, address_index=index, hrp=hrp) except Exception as e: raise WalletError(str(e)) + else: + raise WalletError(f"Unsupported wallet file type: [{path.suffix}]. Supported types are: `.pem` and `.json`.") def _get_address_hrp(args: Any) -> str: diff --git a/multiversx_sdk_cli/config_wallet.py b/multiversx_sdk_cli/config_wallet.py new file mode 100644 index 00000000..7a3e54eb --- /dev/null +++ b/multiversx_sdk_cli/config_wallet.py @@ -0,0 +1,192 @@ +from functools import cache +from pathlib import Path +from typing import Any, Optional + +from multiversx_sdk_cli.constants import SDK_PATH +from multiversx_sdk_cli.errors import ( + AliasAlreadyExistsError, + AliasProtectedError, + InvalidAddressConfigValue, + UnknownWalletAliasError, +) +from multiversx_sdk_cli.utils import read_json_file, write_json_file + +LOCAL_WALLET_CONFIG_PATH = Path("wallets.mxpy.json").resolve() +GLOBAL_WALLET_CONFIG_PATH = SDK_PATH / "wallets.mxpy.json" + + +def get_defaults() -> dict[str, str]: + """ + Not all values are required for a config to be valid. + + Valid config for PEM wallets: + ``` + { + "path": "/path/to/wallet.pem", + "index": "0" # optional, defaults to 0 + } + ``` + + Valid config for KEYSTORE wallets: + ``` + { + "path": "/path/to/wallet.json", + "index": "0" # optional, defaults to 0 + } + ``` + + For keystore wallets, you'll be prompted to enter the password when using the wallet. + """ + return { + "path": "", + "index": "0", + } + + +@cache +def get_value(name: str, alias: str) -> str: + _guard_valid_name(name) + data = read_wallet_config_file() + available_wallets = data.get("wallets", {}) + + wallet = available_wallets.get(alias, None) + if wallet is None: + raise UnknownWalletAliasError(alias) + + default_value = get_defaults()[name] + value = wallet.get(name, default_value) + assert isinstance(value, str) + return value + + +def _guard_valid_name(name: str): + if name not in get_defaults().keys(): + raise InvalidAddressConfigValue(f"Key is not present in wallet config: [{name}]") + + +def get_active_wallet() -> dict[str, str]: + """Returns the active wallet configuration.""" + data = read_wallet_config_file() + addresses: dict[str, Any] = data.get("wallets", {}) + active_address: str = data.get("active", "default") + result: dict[str, str] = addresses.get(active_address, {}) + + return result + + +@cache +def read_wallet_config_file() -> dict[str, Any]: + config_path = resolve_wallet_config_path() + if config_path.exists(): + data: dict[str, Any] = read_json_file(config_path) + return data + return dict() + + +def resolve_wallet_config_path() -> Path: + if LOCAL_WALLET_CONFIG_PATH.is_file(): + return LOCAL_WALLET_CONFIG_PATH + return GLOBAL_WALLET_CONFIG_PATH + + +def set_value(name: str, value: str, alias: str): + """Sets a key-value pair in the specified wallet config.""" + _guard_valid_name(name) + data = read_wallet_config_file() + available_wallets = data.get("wallets", {}) + + wallet = available_wallets.get(alias, None) + if wallet is None: + raise UnknownWalletAliasError(alias) + + wallet[name] = value + available_wallets[alias] = wallet + data["wallets"] = available_wallets + _write_file(data) + + # active_env = data.get("active", "default") + # data.setdefault("wallets", {}) + # data["wallets"].setdefault(active_env, {}) + # data["wallets"][active_env][name] = value + # _write_file(data) + + +def _write_file(data: dict[str, Any]): + env_path = resolve_wallet_config_path() + write_json_file(str(env_path), data) + + +def set_active(name: str): + """Switches to the wallet configuration with the given name.""" + data = read_wallet_config_file() + _guard_valid_wallet_name(data, name) + data["active"] = name + _write_file(data) + + +def _guard_valid_wallet_name(env: Any, name: str): + envs = env.get("wallets", {}) + if name not in envs: + raise UnknownWalletAliasError(name) + + +def create_new_wallet_config(name: str, path: Optional[str] = None, template: Optional[str] = None): + """Creates a new wallet config with the given name and optional template and sets it as the default wallet.""" + data = read_wallet_config_file() + _guard_alias_unique(data, name) + new_wallet = {} + if template: + _guard_valid_wallet_name(data, template) + new_wallet = data["wallets"][template] + + if path: + new_wallet["path"] = path + + data["active"] = name + data.setdefault("wallets", {}) + data["wallets"][name] = new_wallet + _write_file(data) + + +def _guard_alias_unique(env: Any, name: str): + envs = env.get("wallets", {}) + if name in envs: + raise AliasAlreadyExistsError(name) + + +def delete_config_value(key: str, alias: str): + """Deletes a key-value pair of the specified wallet config.""" + _guard_valid_name(key) + + data = read_wallet_config_file() + available_wallets = data.get("wallets", {}) + + wallet = available_wallets.get(alias, None) + if wallet is None: + raise UnknownWalletAliasError(alias) + + del wallet[key] + available_wallets[alias] = wallet + data["wallets"] = available_wallets + _write_file(data) + + # active_env = data.get("active", "default") + # data.setdefault("wallets", {}) + # data["wallets"].setdefault(active_env, {}) + # del data["wallets"][active_env][key] + # _write_file(data) + + +def delete_alias(name: str): + """Deletes the wallet configuration with the given name.""" + _guard_valid_alias_deletion(name) + data = read_wallet_config_file() + data["wallets"].pop(name, None) + if data["active"] == name: + data["active"] = "default" + _write_file(data) + + +def _guard_valid_alias_deletion(name: str): + if name == "default": + raise AliasProtectedError(name) diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 72275099..d03c7983 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -188,7 +188,7 @@ def __init__(self, name: str): super().__init__(f"Environment entry already exists: {name}.") -class UnknownAddressAliasError(KnownError): +class UnknownWalletAliasError(KnownError): def __init__(self, name: str): super().__init__(f"Alias is not known: {name}.") diff --git a/multiversx_sdk_cli/tests/test_cli_default_wallet.py b/multiversx_sdk_cli/tests/test_cli_default_wallet.py index d9631123..ba806e3c 100644 --- a/multiversx_sdk_cli/tests/test_cli_default_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_default_wallet.py @@ -6,15 +6,15 @@ from multiversx_sdk_cli.cli import main -def test_empty_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): - test_file = tmp_path / "addresses.mxpy.json" +def test_empty_wallet_config(capsys: Any, monkeypatch: Any, tmp_path: Path): + test_file = tmp_path / "wallets.mxpy.json" test_file.write_text("{}") - import multiversx_sdk_cli.address_config + import multiversx_sdk_cli.config_wallet - monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) - monkeypatch.setattr(multiversx_sdk_cli.address_config, "GLOBAL_ADDRESS_CONFIG_PATH", test_file) - multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() + monkeypatch.setattr(multiversx_sdk_cli.config_wallet, "LOCAL_WALLET_CONFIG_PATH", test_file) + monkeypatch.setattr(multiversx_sdk_cli.config_wallet, "GLOBAL_WALLET_CONFIG_PATH", test_file) + multiversx_sdk_cli.config_wallet.read_wallet_config_file.cache_clear() return_code = main( [ @@ -55,19 +55,19 @@ def test_empty_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): ) assert return_code out = _read_stdout(capsys) - assert "Address config file is empty." in out + assert "Wallet config file is empty." in out def test_without_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): # Ensure the address config file does not exist; if the actual name is used, when running the tests locally, it will fail with a different error message - test_file = tmp_path / "test-addresses.mxpy.json" + test_file = tmp_path / "test-wallets.mxpy.json" assert not test_file.exists() - import multiversx_sdk_cli.address_config + import multiversx_sdk_cli.config_wallet - monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) - monkeypatch.setattr(multiversx_sdk_cli.address_config, "GLOBAL_ADDRESS_CONFIG_PATH", test_file) - multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() + monkeypatch.setattr(multiversx_sdk_cli.config_wallet, "LOCAL_WALLET_CONFIG_PATH", test_file) + monkeypatch.setattr(multiversx_sdk_cli.config_wallet, "GLOBAL_WALLET_CONFIG_PATH", test_file) + multiversx_sdk_cli.config_wallet.read_wallet_config_file.cache_clear() return_code = main( [ @@ -108,86 +108,26 @@ def test_without_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): ) assert return_code out = _read_stdout(capsys) - assert "The address config file was not found." in out + assert "The wallet config file was not found." in out def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path): - test_file = tmp_path / "addresses.mxpy.json" - json_file = { - "active": "alice", - "addresses": { - "alice": { - "path": "/example/to/wallet.pem", - "index": "0", - }, - }, - } - test_file.write_text(json.dumps(json_file)) - - import multiversx_sdk_cli.address_config - - monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) - monkeypatch.setattr(multiversx_sdk_cli.address_config, "GLOBAL_ADDRESS_CONFIG_PATH", test_file) - multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() - - return_code = main( - [ - "tx", - "new", - "--receiver", - "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--gas-limit", - "50000", - "--nonce", - "0", - "--chain", - "D", - ] - ) - assert return_code - out = _read_stdout(capsys) - assert "'kind' field must be set in the address config." in out - - # Clear the captured content - capsys.readouterr() - - return_code = main( - [ - "tx", - "new", - "--receiver", - "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--gas-limit", - "50000", - "--sender", - "alice", - "--nonce", - "0", - "--chain", - "D", - ] - ) - assert return_code - out = _read_stdout(capsys) - assert "'kind' field must be set in the address config." in out - - # Clear the captured content - capsys.readouterr() + test_file = tmp_path / "wallets.mxpy.json" + import multiversx_sdk_cli.config_wallet json_file = { "active": "alice", - "addresses": { + "wallets": { "alice": { - "kind": "pem", "index": "0", }, }, } test_file.write_text(json.dumps(json_file)) - monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) - monkeypatch.setattr(multiversx_sdk_cli.address_config, "GLOBAL_ADDRESS_CONFIG_PATH", test_file) - multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() + monkeypatch.setattr(multiversx_sdk_cli.config_wallet, "LOCAL_WALLET_CONFIG_PATH", test_file) + monkeypatch.setattr(multiversx_sdk_cli.config_wallet, "GLOBAL_WALLET_CONFIG_PATH", test_file) + multiversx_sdk_cli.config_wallet.read_wallet_config_file.cache_clear() return_code = main( [ @@ -205,7 +145,7 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path ) assert return_code out = _read_stdout(capsys) - assert "'path' field must be set in the address config." in out + assert "'path' field must be set in the wallet config." in out # Clear the captured content capsys.readouterr() @@ -228,14 +168,14 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path ) assert return_code out = _read_stdout(capsys) - assert "'path' field must be set in the address config." in out + assert "'path' field must be set in the wallet config." in out # Clear the captured content capsys.readouterr() json_file = { "active": "alice", - "addresses": { + "wallets": { "alice": { "kind": "keystore", "path": "/example/to/wallet.json", @@ -245,8 +185,8 @@ def test_incomplete_address_config(capsys: Any, monkeypatch: Any, tmp_path: Path } test_file.write_text(json.dumps(json_file)) - monkeypatch.setattr(multiversx_sdk_cli.address_config, "LOCAL_ADDRESS_CONFIG_PATH", test_file) - multiversx_sdk_cli.address_config.read_address_config_file.cache_clear() + monkeypatch.setattr(multiversx_sdk_cli.config_wallet, "LOCAL_WALLET_CONFIG_PATH", test_file) + multiversx_sdk_cli.config_wallet.read_wallet_config_file.cache_clear() monkeypatch.setattr(cli_shared, "getpass", lambda *args, **kwargs: "") From 2ccbda5d959a91933c939fddf6047228c6dd5bd8 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 24 Jun 2025 17:48:18 +0300 Subject: [PATCH 56/64] rename env to config-env --- CLI.md.sh | 18 ++++++------- .../base_transactions_controller.py | 2 +- multiversx_sdk_cli/cli.py | 6 ++--- .../{cli_env.py => cli_config_env.py} | 26 +++++++++++-------- multiversx_sdk_cli/cli_contracts.py | 2 +- multiversx_sdk_cli/cli_get.py | 2 +- multiversx_sdk_cli/cli_shared.py | 2 +- multiversx_sdk_cli/cli_wallet.py | 2 +- multiversx_sdk_cli/{env.py => config_env.py} | 0 multiversx_sdk_cli/delegation.py | 2 +- multiversx_sdk_cli/dns.py | 2 +- 11 files changed, 34 insertions(+), 30 deletions(-) rename multiversx_sdk_cli/{cli_env.py => cli_config_env.py} (77%) rename multiversx_sdk_cli/{env.py => config_env.py} (100%) diff --git a/CLI.md.sh b/CLI.md.sh index f7b492bb..09684b6c 100755 --- a/CLI.md.sh +++ b/CLI.md.sh @@ -185,15 +185,15 @@ generate() { command "Governance.GetConfig" "governance get-config" command "Governance.GetDelegatedVoteInfo" "governance get-delegated-vote-info" - group "Environment" "env" - command "Environment.New" "env new" - command "Environment.Set" "env set" - command "Environment.Get" "env get" - command "Environment.Dump" "env dump" - command "Environment.Switch" "env switch" - command "Environment.List" "env list" - command "Environment.Remove" "env remove" - command "Environment.Reset" "env reset" + group "ConfigEnv" "config-env" + command "ConfigEnv.New" "config-env new" + command "ConfigEnv.Set" "config-env set" + command "ConfigEnv.Get" "config-env get" + command "ConfigEnv.Dump" "config-env dump" + command "ConfigEnv.Switch" "config-env switch" + command "ConfigEnv.List" "config-env list" + command "ConfigEnv.Remove" "config-env remove" + command "ConfigEnv.Reset" "config-env reset" group "ConfigWallet" "config-wallet" command "ConfigWallet.New" "config-wallet new" diff --git a/multiversx_sdk_cli/base_transactions_controller.py b/multiversx_sdk_cli/base_transactions_controller.py index 7c8203f8..8db775a6 100644 --- a/multiversx_sdk_cli/base_transactions_controller.py +++ b/multiversx_sdk_cli/base_transactions_controller.py @@ -10,6 +10,7 @@ StringValue, ) +from multiversx_sdk_cli.config_env import get_address_hrp from multiversx_sdk_cli.constants import ( ADDRESS_PREFIX, EXTRA_GAS_LIMIT_FOR_GUARDED_TRANSACTIONS, @@ -21,7 +22,6 @@ TRUE_STR_LOWER, ) from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.env import get_address_hrp from multiversx_sdk_cli.errors import BadUserInput, TransactionSigningError from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 0e2a2639..8ad91fd5 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -10,13 +10,13 @@ from rich.logging import RichHandler import multiversx_sdk_cli.cli_config +import multiversx_sdk_cli.cli_config_env import multiversx_sdk_cli.cli_config_wallet import multiversx_sdk_cli.cli_contracts import multiversx_sdk_cli.cli_data import multiversx_sdk_cli.cli_delegation import multiversx_sdk_cli.cli_deps import multiversx_sdk_cli.cli_dns -import multiversx_sdk_cli.cli_env import multiversx_sdk_cli.cli_faucet import multiversx_sdk_cli.cli_get import multiversx_sdk_cli.cli_governance @@ -30,8 +30,8 @@ import multiversx_sdk_cli.version from multiversx_sdk_cli import config, errors, utils, ux from multiversx_sdk_cli.cli_shared import set_proxy_from_config_if_not_provided +from multiversx_sdk_cli.config_env import get_address_hrp from multiversx_sdk_cli.constants import LOG_LEVELS, SDK_PATH -from multiversx_sdk_cli.env import get_address_hrp logger = logging.getLogger("cli") @@ -140,7 +140,7 @@ def setup_parser(args: list[str]): commands.append(multiversx_sdk_cli.cli_faucet.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_multisig.setup_parser(args, subparsers)) commands.append(multiversx_sdk_cli.cli_governance.setup_parser(args, subparsers)) - commands.append(multiversx_sdk_cli.cli_env.setup_parser(subparsers)) + commands.append(multiversx_sdk_cli.cli_config_env.setup_parser(subparsers)) commands.append(multiversx_sdk_cli.cli_get.setup_parser(subparsers)) parser.epilog = """ diff --git a/multiversx_sdk_cli/cli_env.py b/multiversx_sdk_cli/cli_config_env.py similarity index 77% rename from multiversx_sdk_cli/cli_env.py rename to multiversx_sdk_cli/cli_config_env.py index 4e3614a9..2584bd21 100644 --- a/multiversx_sdk_cli/cli_env.py +++ b/multiversx_sdk_cli/cli_config_env.py @@ -3,7 +3,7 @@ from typing import Any from multiversx_sdk_cli import cli_shared -from multiversx_sdk_cli.env import ( +from multiversx_sdk_cli.config_env import ( create_new_env, delete_env, delete_value, @@ -23,12 +23,12 @@ def setup_parser(subparsers: Any) -> Any: parser = cli_shared.add_group_subparser( - subparsers, "env", "Configure MultiversX CLI to use specific environment values." + subparsers, "config-env", "Configure MultiversX CLI to use specific environment values." ) subparsers = parser.add_subparsers() sub = cli_shared.add_command_subparser( - subparsers, "env", "new", "Creates a new environment and sets it as the active environment." + subparsers, "config-env", "new", "Creates a new environment and sets it as the active environment." ) _add_name_arg(sub) sub.add_argument( @@ -38,16 +38,20 @@ def setup_parser(subparsers: Any) -> Any: ) sub.set_defaults(func=new_env) - sub = cli_shared.add_command_subparser(subparsers, "env", "get", "Gets an env value from the active environment.") + sub = cli_shared.add_command_subparser( + subparsers, "config-env", "get", "Gets an env value from the active environment." + ) _add_name_arg(sub) sub.set_defaults(func=get_env_value) - sub = cli_shared.add_command_subparser(subparsers, "env", "set", "Sets an env value for the active environment.") + sub = cli_shared.add_command_subparser( + subparsers, "config-env", "set", "Sets an env value for the active environment." + ) _add_name_arg(sub) sub.add_argument("value", type=str, help="the new value") sub.set_defaults(func=set_env_value) - sub = cli_shared.add_command_subparser(subparsers, "env", "dump", "Dumps the active environment.") + sub = cli_shared.add_command_subparser(subparsers, "config-env", "dump", "Dumps the active environment.") sub.add_argument( "--default", required=False, @@ -57,21 +61,21 @@ def setup_parser(subparsers: Any) -> Any: sub.set_defaults(func=dump) sub = cli_shared.add_command_subparser( - subparsers, "env", "delete", "Deletes an env value from the active environment." + subparsers, "config-env", "delete", "Deletes an env value from the active environment." ) _add_name_arg(sub) sub.set_defaults(func=delete_env_value) - sub = cli_shared.add_command_subparser(subparsers, "env", "switch", "Switch to a different environment.") + sub = cli_shared.add_command_subparser(subparsers, "config-env", "switch", "Switch to a different environment.") _add_name_arg(sub) sub.set_defaults(func=switch_env) - sub = cli_shared.add_command_subparser(subparsers, "env", "list", "List available environments") + sub = cli_shared.add_command_subparser(subparsers, "config-env", "list", "List available environments") sub.set_defaults(func=list_envs) sub = cli_shared.add_command_subparser( subparsers, - "env", + "config-env", "remove", "Deletes an environment from the env file. Will switch to default env.", ) @@ -80,7 +84,7 @@ def setup_parser(subparsers: Any) -> Any: sub = cli_shared.add_command_subparser( subparsers, - "env", + "config-env", "reset", "Deletes the environment file. Default env will be used.", ) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 97b2515c..c79ee17e 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -25,11 +25,11 @@ ) from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.config import get_config_for_network_providers +from multiversx_sdk_cli.config_env import MxpyEnv from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS from multiversx_sdk_cli.contract_verification import trigger_contract_verification from multiversx_sdk_cli.contracts import SmartContract from multiversx_sdk_cli.docker import is_docker_installed, run_docker -from multiversx_sdk_cli.env import MxpyEnv from multiversx_sdk_cli.errors import DockerMissingError from multiversx_sdk_cli.ux import show_warning diff --git a/multiversx_sdk_cli/cli_get.py b/multiversx_sdk_cli/cli_get.py index 8982d641..45bd6d3a 100644 --- a/multiversx_sdk_cli/cli_get.py +++ b/multiversx_sdk_cli/cli_get.py @@ -7,7 +7,7 @@ from multiversx_sdk_cli import cli_shared from multiversx_sdk_cli.config import get_config_for_network_providers -from multiversx_sdk_cli.env import MxpyEnv +from multiversx_sdk_cli.config_env import MxpyEnv from multiversx_sdk_cli.errors import ( ArgumentsNotProvidedError, BadUsage, diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index b84a203e..d94d47fb 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -28,6 +28,7 @@ load_password, load_relayer_password, ) +from multiversx_sdk_cli.config_env import MxpyEnv, get_address_hrp from multiversx_sdk_cli.config_wallet import ( get_active_wallet, read_wallet_config_file, @@ -38,7 +39,6 @@ DEFAULT_TX_VERSION, TCS_SERVICE_ID, ) -from multiversx_sdk_cli.env import MxpyEnv, get_address_hrp from multiversx_sdk_cli.errors import ( AddressConfigFileError, ArgumentsNotProvidedError, diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 44bf02ad..488d814f 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -9,8 +9,8 @@ from multiversx_sdk.core.address import get_shard_of_pubkey from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.config_env import get_address_hrp from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS -from multiversx_sdk_cli.env import get_address_hrp from multiversx_sdk_cli.errors import ( BadUsage, BadUserInput, diff --git a/multiversx_sdk_cli/env.py b/multiversx_sdk_cli/config_env.py similarity index 100% rename from multiversx_sdk_cli/env.py rename to multiversx_sdk_cli/config_env.py diff --git a/multiversx_sdk_cli/delegation.py b/multiversx_sdk_cli/delegation.py index e87e2862..2a158a59 100644 --- a/multiversx_sdk_cli/delegation.py +++ b/multiversx_sdk_cli/delegation.py @@ -8,7 +8,7 @@ from multiversx_sdk.abi import BigUIntValue, Serializer from multiversx_sdk_cli.base_transactions_controller import BaseTransactionsController -from multiversx_sdk_cli.env import get_address_hrp +from multiversx_sdk_cli.config_env import get_address_hrp from multiversx_sdk_cli.errors import BadUsage from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 8cda4db5..50955e34 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -14,8 +14,8 @@ validate_chain_id_args, validate_transaction_args, ) +from multiversx_sdk_cli.config_env import get_address_hrp from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX -from multiversx_sdk_cli.env import get_address_hrp from multiversx_sdk_cli.transactions import TransactionsController MaxNumShards = 256 From 9742630900f0ed5c877bce0491b4b82e8a8310e9 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 24 Jun 2025 17:55:08 +0300 Subject: [PATCH 57/64] generate CLI file --- CLI.md | 58 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/CLI.md b/CLI.md index 52c1fa51..9eccefb8 100644 --- a/CLI.md +++ b/CLI.md @@ -23,7 +23,7 @@ See: COMMAND GROUPS: - {config-wallet,contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet,multisig,governance,env,get} + {config-wallet,contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet,multisig,governance,config-env,get} TOP-LEVEL OPTIONS: -h, --help show this help message and exit @@ -51,7 +51,7 @@ dns Operations related to the Domain Name Service faucet Get xEGLD on Devnet or Testnet multisig Deploy and interact with the Multisig Smart Contract governance Propose, vote and interact with the governance contract. -env Configure MultiversX CLI to use specific environment values. +config-env Configure MultiversX CLI to use specific environment values. get Get info from the network. ``` @@ -5632,12 +5632,12 @@ options: --proxy PROXY 🔗 the URL of the proxy ``` -## Group **Environment** +## Group **ConfigEnv** ``` -$ mxpy env --help -usage: mxpy env COMMAND [-h] ... +$ mxpy config-env --help +usage: mxpy config-env COMMAND [-h] ... Configure MultiversX CLI to use specific environment values. @@ -5661,12 +5661,12 @@ remove Deletes an environment from the env file. Will sw reset Deletes the environment file. Default env will be used. ``` -### Environment.New +### ConfigEnv.New ``` -$ mxpy env new --help -usage: mxpy env new [-h] ... +$ mxpy config-env new --help +usage: mxpy config-env new [-h] ... Creates a new environment and sets it as the active environment. @@ -5678,12 +5678,12 @@ options: --template TEMPLATE an environment from which to create the new environment ``` -### Environment.Set +### ConfigEnv.Set ``` -$ mxpy env set --help -usage: mxpy env set [-h] ... +$ mxpy config-env set --help +usage: mxpy config-env set [-h] ... Sets an env value for the active environment. @@ -5695,12 +5695,12 @@ options: -h, --help show this help message and exit ``` -### Environment.Get +### ConfigEnv.Get ``` -$ mxpy env get --help -usage: mxpy env get [-h] ... +$ mxpy config-env get --help +usage: mxpy config-env get [-h] ... Gets an env value from the active environment. @@ -5711,12 +5711,12 @@ options: -h, --help show this help message and exit ``` -### Environment.Dump +### ConfigEnv.Dump ``` -$ mxpy env dump --help -usage: mxpy env dump [-h] ... +$ mxpy config-env dump --help +usage: mxpy config-env dump [-h] ... Dumps the active environment. @@ -5725,12 +5725,12 @@ options: --default dumps the default environment instead of the active one. ``` -### Environment.Switch +### ConfigEnv.Switch ``` -$ mxpy env switch --help -usage: mxpy env switch [-h] ... +$ mxpy config-env switch --help +usage: mxpy config-env switch [-h] ... Switch to a different environment. @@ -5741,12 +5741,12 @@ options: -h, --help show this help message and exit ``` -### Environment.List +### ConfigEnv.List ``` -$ mxpy env list --help -usage: mxpy env list [-h] ... +$ mxpy config-env list --help +usage: mxpy config-env list [-h] ... List available environments @@ -5754,12 +5754,12 @@ options: -h, --help show this help message and exit ``` -### Environment.Remove +### ConfigEnv.Remove ``` -$ mxpy env remove --help -usage: mxpy env remove [-h] ... +$ mxpy config-env remove --help +usage: mxpy config-env remove [-h] ... Deletes an environment from the env file. Will switch to default env. @@ -5770,12 +5770,12 @@ options: -h, --help show this help message and exit ``` -### Environment.Reset +### ConfigEnv.Reset ``` -$ mxpy env reset --help -usage: mxpy env reset [-h] ... +$ mxpy config-env reset --help +usage: mxpy config-env reset [-h] ... Deletes the environment file. Default env will be used. From 61818679c51b6cd156a538a379cec2599336fe00 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 25 Jun 2025 11:17:00 +0300 Subject: [PATCH 58/64] refactoring config-env --- CLI.md | 28 +++++------ multiversx_sdk_cli/cli_config_env.py | 31 +++++++----- multiversx_sdk_cli/config_env.py | 75 +++++++++++++++++++++------- 3 files changed, 90 insertions(+), 44 deletions(-) diff --git a/CLI.md b/CLI.md index 9eccefb8..68457d5b 100644 --- a/CLI.md +++ b/CLI.md @@ -5651,13 +5651,13 @@ OPTIONS: COMMANDS summary ---------------- new Creates a new environment and sets it as the active environment. -get Gets an env value from the active environment. -set Sets an env value for the active environment. +get Gets an env value from the specified environment. +set Sets an env value for the specified environment. dump Dumps the active environment. -delete Deletes an env value from the active environment. +delete Deletes an env value from the specified environment. switch Switch to a different environment. list List available environments -remove Deletes an environment from the env file. Will switch to default env. +remove Deletes an environment from the env file. Use `mxpy config-env switch` to move to another env. reset Deletes the environment file. Default env will be used. ``` @@ -5671,7 +5671,7 @@ usage: mxpy config-env new [-h] ... Creates a new environment and sets it as the active environment. positional arguments: - name the name of the configuration entry + name the name of the new environment options: -h, --help show this help message and exit @@ -5685,7 +5685,7 @@ options: $ mxpy config-env set --help usage: mxpy config-env set [-h] ... -Sets an env value for the active environment. +Sets an env value for the specified environment. positional arguments: name the name of the configuration entry @@ -5693,6 +5693,7 @@ positional arguments: options: -h, --help show this help message and exit + --env ENV the name of the environment to operate on ``` ### ConfigEnv.Get @@ -5702,13 +5703,14 @@ options: $ mxpy config-env get --help usage: mxpy config-env get [-h] ... -Gets an env value from the active environment. +Gets an env value from the specified environment. positional arguments: name the name of the configuration entry options: -h, --help show this help message and exit + --env ENV the name of the environment to operate on ``` ### ConfigEnv.Dump @@ -5734,11 +5736,9 @@ usage: mxpy config-env switch [-h] ... Switch to a different environment. -positional arguments: - name the name of the configuration entry - options: -h, --help show this help message and exit + --env ENV the name of the environment to operate on ``` ### ConfigEnv.List @@ -5761,13 +5761,11 @@ options: $ mxpy config-env remove --help usage: mxpy config-env remove [-h] ... -Deletes an environment from the env file. Will switch to default env. - -positional arguments: - environment The environment to remove from env file. +Deletes an environment from the env file. Use `mxpy config-env switch` to move to another env. options: - -h, --help show this help message and exit + -h, --help show this help message and exit + --env ENV the name of the environment to operate on ``` ### ConfigEnv.Reset diff --git a/multiversx_sdk_cli/cli_config_env.py b/multiversx_sdk_cli/cli_config_env.py index 2584bd21..e893aa13 100644 --- a/multiversx_sdk_cli/cli_config_env.py +++ b/multiversx_sdk_cli/cli_config_env.py @@ -30,7 +30,7 @@ def setup_parser(subparsers: Any) -> Any: sub = cli_shared.add_command_subparser( subparsers, "config-env", "new", "Creates a new environment and sets it as the active environment." ) - _add_name_arg(sub) + sub.add_argument("name", type=str, help="the name of the new environment") sub.add_argument( "--template", required=False, @@ -39,16 +39,18 @@ def setup_parser(subparsers: Any) -> Any: sub.set_defaults(func=new_env) sub = cli_shared.add_command_subparser( - subparsers, "config-env", "get", "Gets an env value from the active environment." + subparsers, "config-env", "get", "Gets an env value from the specified environment." ) _add_name_arg(sub) + _add_env_arg(sub) sub.set_defaults(func=get_env_value) sub = cli_shared.add_command_subparser( - subparsers, "config-env", "set", "Sets an env value for the active environment." + subparsers, "config-env", "set", "Sets an env value for the specified environment." ) _add_name_arg(sub) sub.add_argument("value", type=str, help="the new value") + _add_env_arg(sub) sub.set_defaults(func=set_env_value) sub = cli_shared.add_command_subparser(subparsers, "config-env", "dump", "Dumps the active environment.") @@ -61,13 +63,14 @@ def setup_parser(subparsers: Any) -> Any: sub.set_defaults(func=dump) sub = cli_shared.add_command_subparser( - subparsers, "config-env", "delete", "Deletes an env value from the active environment." + subparsers, "config-env", "delete", "Deletes an env value from the specified environment." ) _add_name_arg(sub) + _add_env_arg(sub) sub.set_defaults(func=delete_env_value) sub = cli_shared.add_command_subparser(subparsers, "config-env", "switch", "Switch to a different environment.") - _add_name_arg(sub) + _add_env_arg(sub) sub.set_defaults(func=switch_env) sub = cli_shared.add_command_subparser(subparsers, "config-env", "list", "List available environments") @@ -77,9 +80,9 @@ def setup_parser(subparsers: Any) -> Any: subparsers, "config-env", "remove", - "Deletes an environment from the env file. Will switch to default env.", + "Deletes an environment from the env file. Use `mxpy config-env switch` to move to another env.", ) - sub.add_argument("environment", type=str, help="The environment to remove from env file.") + _add_env_arg(sub) sub.set_defaults(func=remove_env_entry) sub = cli_shared.add_command_subparser( @@ -98,6 +101,10 @@ def _add_name_arg(sub: Any): sub.add_argument("name", type=str, help="the name of the configuration entry") +def _add_env_arg(sub: Any): + sub.add_argument("--env", required=True, type=str, help="the name of the environment to operate on") + + def dump(args: Any): if args.default: dump_out_json(get_defaults()) @@ -106,16 +113,16 @@ def dump(args: Any): def get_env_value(args: Any): - value = get_value(args.name) + value = get_value(args.name, args.env) print(value) def set_env_value(args: Any): - set_value(args.name, args.value) + set_value(args.name, args.value, args.env) def delete_env_value(args: Any): - delete_value(args.name) + delete_value(args.name, args.env) def new_env(args: Any): @@ -124,7 +131,7 @@ def new_env(args: Any): def switch_env(args: Any): - set_active(args.name) + set_active(args.env) dump_out_json(get_active_env()) @@ -139,7 +146,7 @@ def remove_env_entry(args: Any): logger.info("Environment file not found. Aborting...") return - delete_env(args.environment) + delete_env(args.env) def delete_env_file(args: Any): diff --git a/multiversx_sdk_cli/config_env.py b/multiversx_sdk_cli/config_env.py index 40f003a5..8629a9ca 100644 --- a/multiversx_sdk_cli/config_env.py +++ b/multiversx_sdk_cli/config_env.py @@ -46,22 +46,47 @@ def get_defaults() -> dict[str, str]: @cache def get_address_hrp() -> str: - return get_value("default_address_hrp") + """ + Returns the HRP for the active environment. + If not set, it returns the default value. + """ + data = read_env_file() + active_env_name: str = data.get("active", "default") + return get_value("default_address_hrp", active_env_name) @cache def get_proxy_url() -> str: - return get_value("proxy_url") + """ + Returns the proxy URL for the active environment. + If not set, it returns an empty string. + """ + data = read_env_file() + active_env_name: str = data.get("active", "default") + return get_value("proxy_url", active_env_name) @cache def get_explorer_url() -> str: - return get_value("explorer_url") + """ + Returns the explorer URL for the active environment. + If not set, it returns an empty string. + """ + data = read_env_file() + active_env_name: str = data.get("active", "default") + return get_value("explorer_url", active_env_name) @cache def get_confirmation_setting() -> bool: - confirmation_value = get_value("ask_confirmation") + """ + Returns the confirmation setting for the active environment. + If not set, it defaults to False. + """ + data = read_env_file() + active_env_name: str = data.get("active", "default") + + confirmation_value = get_value("ask_confirmation", active_env_name) if confirmation_value.lower() in ["true", "yes", "1"]: return True elif confirmation_value.lower() in ["false", "no", "0"]: @@ -71,11 +96,17 @@ def get_confirmation_setting() -> bool: @cache -def get_value(name: str) -> str: +def get_value(name: str, env_name: str) -> str: _guard_valid_name(name) - data = get_active_env() + data = read_env_file() + + envs = data.get("environments", {}) + env = envs.get(env_name, None) + if env is None: + raise UnknownEnvironmentError(env_name) + default_value = get_defaults()[name] - value = data.get(name, default_value) + value = env.get(name, default_value) assert isinstance(value, str) return value @@ -109,13 +140,18 @@ def resolve_env_path() -> Path: return GLOBAL_ENV_PATH -def set_value(name: str, value: Any): +def set_value(name: str, value: str, env_name: str): _guard_valid_name(name) data = read_env_file() - active_env = data.get("active", "default") - data.setdefault("environments", {}) - data["environments"].setdefault(active_env, {}) - data["environments"][active_env][name] = value + + envs = data.get("environments", {}) + env = envs.get(env_name, None) + if env is None: + raise UnknownEnvironmentError(env_name) + + env[name] = value + envs[env_name] = env + data["environments"] = envs write_file(data) @@ -124,14 +160,19 @@ def write_file(data: dict[str, Any]): write_json_file(str(env_path), data) -def delete_value(name: str): +def delete_value(name: str, env_name: str): """Deletes a key-value pair of the active env.""" _guard_valid_env_deletion(name) data = read_env_file() - active_env = data.get("active", "default") - data.setdefault("environments", {}) - data["environments"].setdefault(active_env, {}) - del data["environments"][active_env][name] + + envs = data.get("environments", {}) + env = envs.get(env_name, None) + if env is None: + raise UnknownEnvironmentError(env_name) + + del env[name] + envs[env_name] = env + data["environments"] = envs write_file(data) From 340f2c47ac481fee52a4cdbed35ae7bb41f452ac Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 25 Jun 2025 11:18:02 +0300 Subject: [PATCH 59/64] remove commented code --- multiversx_sdk_cli/config_wallet.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/multiversx_sdk_cli/config_wallet.py b/multiversx_sdk_cli/config_wallet.py index 7a3e54eb..1d3f04c5 100644 --- a/multiversx_sdk_cli/config_wallet.py +++ b/multiversx_sdk_cli/config_wallet.py @@ -104,12 +104,6 @@ def set_value(name: str, value: str, alias: str): data["wallets"] = available_wallets _write_file(data) - # active_env = data.get("active", "default") - # data.setdefault("wallets", {}) - # data["wallets"].setdefault(active_env, {}) - # data["wallets"][active_env][name] = value - # _write_file(data) - def _write_file(data: dict[str, Any]): env_path = resolve_wallet_config_path() @@ -170,12 +164,6 @@ def delete_config_value(key: str, alias: str): data["wallets"] = available_wallets _write_file(data) - # active_env = data.get("active", "default") - # data.setdefault("wallets", {}) - # data["wallets"].setdefault(active_env, {}) - # del data["wallets"][active_env][key] - # _write_file(data) - def delete_alias(name: str): """Deletes the wallet configuration with the given name.""" From dafbd5d95093fc21770b33e2fcc6113a51c869df Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 25 Jun 2025 11:41:20 +0300 Subject: [PATCH 60/64] update windows workflow to use windows-2022 --- .github/workflows/build-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 32c1c314..0fa4d9bb 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: - os: [windows-2019] + os: [windows-2022] python-version: [3.11] steps: From d875ddcbd704538216ece554c8870b25e379df41 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Wed, 25 Jun 2025 16:01:06 +0300 Subject: [PATCH 61/64] fixes after review --- CLI.md | 33 +++++++------- CLI.md.sh | 2 +- multiversx_sdk_cli/cli_config_wallet.py | 57 +++++++++++-------------- multiversx_sdk_cli/cli_shared.py | 6 +-- multiversx_sdk_cli/config_wallet.py | 13 +++--- 5 files changed, 51 insertions(+), 60 deletions(-) diff --git a/CLI.md b/CLI.md index 52c1fa51..96424409 100644 --- a/CLI.md +++ b/CLI.md @@ -30,7 +30,7 @@ TOP-LEVEL OPTIONS: -v, --version show program's version number and exit --verbose --log-level {debug,info,warning,error} - default: debug + default: info ---------------------- COMMAND GROUPS summary @@ -5822,12 +5822,11 @@ usage: mxpy config-wallet new [-h] ... Creates a new wallet config and sets it as the active wallet. positional arguments: - alias the alias of the wallet + alias the alias of the wallet options: - -h, --help show this help message and exit - --path PATH the absolute path to the wallet file - --template TEMPLATE a wallet config from which to create the new config + -h, --help show this help message and exit + --path PATH the absolute path to the wallet file ``` ### ConfigWallet.List @@ -5891,31 +5890,31 @@ options: --alias ALIAS the alias of the wallet ``` -### ConfigWallet.Set +### ConfigWallet.Switch ``` -$ mxpy config-wallet delete --help -usage: mxpy config-wallet delete [-h] ... - -Deletes a config value from the specified wallet. +$ mxpy config-wallet switch --help +usage: mxpy config-wallet switch [-h] ... -positional arguments: - value the value to delete for the specified address +Switch to a different wallet. options: -h, --help show this help message and exit --alias ALIAS the alias of the wallet ``` -### ConfigWallet.Switch +### ConfigWallet.Delete ``` -$ mxpy config-wallet switch --help -usage: mxpy config-wallet switch [-h] ... +$ mxpy config-wallet delete --help +usage: mxpy config-wallet delete [-h] ... -Switch to a different wallet. +Deletes a config value from the specified wallet. + +positional arguments: + value the value to delete for the specified address options: -h, --help show this help message and exit @@ -5941,7 +5940,7 @@ options: ``` $ mxpy config-wallet reset --help -usage: mxpy address reset [-h] ... +usage: mxpy config-wallet reset [-h] ... Deletes the config file. No default wallet will be set. diff --git a/CLI.md.sh b/CLI.md.sh index f7b492bb..ceb6f205 100755 --- a/CLI.md.sh +++ b/CLI.md.sh @@ -201,8 +201,8 @@ generate() { command "ConfigWallet.Dump" "config-wallet dump" command "ConfigWallet.Get" "config-wallet get" command "ConfigWallet.Set" "config-wallet set" - command "ConfigWallet.Set" "config-wallet delete" command "ConfigWallet.Switch" "config-wallet switch" + command "ConfigWallet.Delete" "config-wallet delete" command "ConfigWallet.Remove" "config-wallet remove" command "ConfigWallet.Reset" "config-wallet reset" diff --git a/multiversx_sdk_cli/cli_config_wallet.py b/multiversx_sdk_cli/cli_config_wallet.py index e1e2cb83..08c90dc0 100644 --- a/multiversx_sdk_cli/cli_config_wallet.py +++ b/multiversx_sdk_cli/cli_config_wallet.py @@ -11,8 +11,8 @@ get_value, read_wallet_config_file, resolve_wallet_config_path, - set_active, set_value, + switch_wallet, ) from multiversx_sdk_cli.utils import dump_out_json from multiversx_sdk_cli.ux import confirm_continuation @@ -31,15 +31,10 @@ def setup_parser(subparsers: Any) -> Any: ) sub.add_argument("alias", type=str, help="the alias of the wallet") sub.add_argument("--path", type=str, required=False, help="the absolute path to the wallet file") - sub.add_argument( - "--template", - required=False, - help="a wallet config from which to create the new config", - ) sub.set_defaults(func=new_wallet_config) sub = cli_shared.add_command_subparser(subparsers, "config-wallet", "list", "List configured wallets") - sub.set_defaults(func=list_addresses) + sub.set_defaults(func=list_wallets) sub = cli_shared.add_command_subparser(subparsers, "config-wallet", "dump", "Dumps the active wallet.") sub.set_defaults(func=dump) @@ -49,7 +44,7 @@ def setup_parser(subparsers: Any) -> Any: ) sub.add_argument("value", type=str, help="the value to get from the specified wallet (e.g. path)") _add_alias_arg(sub) - sub.set_defaults(func=get_address_config_value) + sub.set_defaults(func=get_wallet_config_value) sub = cli_shared.add_command_subparser( subparsers, "config-wallet", "set", "Sets a config value for the specified wallet." @@ -57,18 +52,18 @@ def setup_parser(subparsers: Any) -> Any: sub.add_argument("key", type=str, help="the key to set for the specified wallet (e.g. index)") sub.add_argument("value", type=str, help="the value to set for the specified key") _add_alias_arg(sub) - sub.set_defaults(func=set_address_config_value) + sub.set_defaults(func=set_wallet_config_value) sub = cli_shared.add_command_subparser( subparsers, "config-wallet", "delete", "Deletes a config value from the specified wallet." ) sub.add_argument("value", type=str, help="the value to delete for the specified address") _add_alias_arg(sub) - sub.set_defaults(func=delete_address_config_value) + sub.set_defaults(func=delete_wallet_config_value) sub = cli_shared.add_command_subparser(subparsers, "config-wallet", "switch", "Switch to a different wallet.") _add_alias_arg(sub) - sub.set_defaults(func=switch_address) + sub.set_defaults(func=switch_wallet_to_active) sub = cli_shared.add_command_subparser( subparsers, @@ -77,15 +72,15 @@ def setup_parser(subparsers: Any) -> Any: "Removes a wallet from the config using the alias. No default wallet will be set. Use `config-wallet switch` to set a new wallet.", ) _add_alias_arg(sub) - sub.set_defaults(func=remove_address) + sub.set_defaults(func=remove_wallet) sub = cli_shared.add_command_subparser( subparsers, - "address", + "config-wallet", "reset", "Deletes the config file. No default wallet will be set.", ) - sub.set_defaults(func=delete_address_config_file) + sub.set_defaults(func=delete_wallet_config_file) parser.epilog = cli_shared.build_group_epilog(subparsers) return subparsers @@ -96,53 +91,53 @@ def _add_alias_arg(sub: Any): def new_wallet_config(args: Any): - create_new_wallet_config(name=args.alias, path=args.path, template=args.template) + create_new_wallet_config(name=args.alias, path=args.path) dump_out_json(get_active_wallet()) -def list_addresses(args: Any): - _ensure_address_config_file_exists() +def list_wallets(args: Any): + _ensure_wallet_config_file_exists() data = read_wallet_config_file() dump_out_json(data) def dump(args: Any): - _ensure_address_config_file_exists() + _ensure_wallet_config_file_exists() dump_out_json(get_active_wallet()) -def get_address_config_value(args: Any): - _ensure_address_config_file_exists() +def get_wallet_config_value(args: Any): + _ensure_wallet_config_file_exists() value = get_value(args.value, args.alias) print(value) -def set_address_config_value(args: Any): - _ensure_address_config_file_exists() +def set_wallet_config_value(args: Any): + _ensure_wallet_config_file_exists() set_value(args.key, args.value, args.alias) -def delete_address_config_value(args: Any): - _ensure_address_config_file_exists() +def delete_wallet_config_value(args: Any): + _ensure_wallet_config_file_exists() delete_config_value(args.value, args.alias) dump_out_json(get_active_wallet()) -def switch_address(args: Any): - _ensure_address_config_file_exists() +def switch_wallet_to_active(args: Any): + _ensure_wallet_config_file_exists() - set_active(args.alias) + switch_wallet(args.alias) dump_out_json(get_active_wallet()) -def remove_address(args: Any): - _ensure_address_config_file_exists() +def remove_wallet(args: Any): + _ensure_wallet_config_file_exists() delete_alias(args.alias) -def delete_address_config_file(args: Any): +def delete_wallet_config_file(args: Any): address_file = resolve_wallet_config_path() if not address_file.is_file(): logger.info("Wallet config file not found. Aborting...") @@ -153,7 +148,7 @@ def delete_address_config_file(args: Any): logger.info("Successfully deleted the address config file.") -def _ensure_address_config_file_exists(): +def _ensure_wallet_config_file_exists(): address_file = resolve_wallet_config_path() if not address_file.is_file(): logger.info("Wallet config file not found. Aborting...") diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index b84a203e..7b4d3c5c 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -382,7 +382,7 @@ def load_wallet_by_alias(alias: str, hrp: str) -> Account: raise UnknownWalletAliasError(alias) logger.info(f"Using sender [{alias}] from wallet config.") - return _load_wallet_from_address_config(wallet=wallet, hrp=hrp) + return _load_wallet_from_wallet_config(wallet=wallet, hrp=hrp) def load_default_wallet(hrp: str) -> Account: @@ -394,10 +394,10 @@ def load_default_wallet(hrp: str) -> Account: alias_of_default_wallet = read_wallet_config_file().get("active", "") logger.info(f"Using sender [{alias_of_default_wallet}] from wallet config.") - return _load_wallet_from_address_config(wallet=active_wallet, hrp=hrp) + return _load_wallet_from_wallet_config(wallet=active_wallet, hrp=hrp) -def _load_wallet_from_address_config(wallet: dict[str, str], hrp: str) -> Account: +def _load_wallet_from_wallet_config(wallet: dict[str, str], hrp: str) -> Account: wallet_path = wallet.get("path", None) if not wallet_path: raise AddressConfigFileError("'path' field must be set in the wallet config.") diff --git a/multiversx_sdk_cli/config_wallet.py b/multiversx_sdk_cli/config_wallet.py index 1d3f04c5..71eb5745 100644 --- a/multiversx_sdk_cli/config_wallet.py +++ b/multiversx_sdk_cli/config_wallet.py @@ -110,7 +110,7 @@ def _write_file(data: dict[str, Any]): write_json_file(str(env_path), data) -def set_active(name: str): +def switch_wallet(name: str): """Switches to the wallet configuration with the given name.""" data = read_wallet_config_file() _guard_valid_wallet_name(data, name) @@ -124,14 +124,11 @@ def _guard_valid_wallet_name(env: Any, name: str): raise UnknownWalletAliasError(name) -def create_new_wallet_config(name: str, path: Optional[str] = None, template: Optional[str] = None): - """Creates a new wallet config with the given name and optional template and sets it as the default wallet.""" +def create_new_wallet_config(name: str, path: Optional[str] = None): + """Creates a new wallet config with the given name and sets it as the default wallet.""" data = read_wallet_config_file() _guard_alias_unique(data, name) new_wallet = {} - if template: - _guard_valid_wallet_name(data, template) - new_wallet = data["wallets"][template] if path: new_wallet["path"] = path @@ -143,8 +140,8 @@ def create_new_wallet_config(name: str, path: Optional[str] = None, template: Op def _guard_alias_unique(env: Any, name: str): - envs = env.get("wallets", {}) - if name in envs: + wallets = env.get("wallets", {}) + if name in wallets: raise AliasAlreadyExistsError(name) From fce7475116b56dc192b1dd1c1ea3af4f7f8641d2 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 26 Jun 2025 10:41:41 +0300 Subject: [PATCH 62/64] rename logger --- multiversx_sdk_cli/cli_config_env.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiversx_sdk_cli/cli_config_env.py b/multiversx_sdk_cli/cli_config_env.py index e893aa13..2c4282d8 100644 --- a/multiversx_sdk_cli/cli_config_env.py +++ b/multiversx_sdk_cli/cli_config_env.py @@ -18,7 +18,7 @@ from multiversx_sdk_cli.utils import dump_out_json from multiversx_sdk_cli.ux import confirm_continuation -logger = logging.getLogger("cli.env") +logger = logging.getLogger("cli.config_env") def setup_parser(subparsers: Any) -> Any: @@ -80,7 +80,7 @@ def setup_parser(subparsers: Any) -> Any: subparsers, "config-env", "remove", - "Deletes an environment from the env file. Use `mxpy config-env switch` to move to another env.", + "Deletes an environment from the env file. Use `mxpy config-env switch` to switch to another env.", ) _add_env_arg(sub) sub.set_defaults(func=remove_env_entry) From 74a6c1ba06427276c2b769734ff5a1355d3f4ecc Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 26 Jun 2025 12:04:04 +0300 Subject: [PATCH 63/64] fix for when no env is configured --- multiversx_sdk_cli/config_env.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/multiversx_sdk_cli/config_env.py b/multiversx_sdk_cli/config_env.py index 8629a9ca..bdbb239d 100644 --- a/multiversx_sdk_cli/config_env.py +++ b/multiversx_sdk_cli/config_env.py @@ -52,6 +52,9 @@ def get_address_hrp() -> str: """ data = read_env_file() active_env_name: str = data.get("active", "default") + + if active_env_name == "default": + return get_defaults()["default_address_hrp"] return get_value("default_address_hrp", active_env_name) @@ -63,6 +66,9 @@ def get_proxy_url() -> str: """ data = read_env_file() active_env_name: str = data.get("active", "default") + + if active_env_name == "default": + return get_defaults()["proxy_url"] return get_value("proxy_url", active_env_name) @@ -74,6 +80,9 @@ def get_explorer_url() -> str: """ data = read_env_file() active_env_name: str = data.get("active", "default") + + if active_env_name == "default": + return get_defaults()["explorer_url"] return get_value("explorer_url", active_env_name) @@ -86,6 +95,9 @@ def get_confirmation_setting() -> bool: data = read_env_file() active_env_name: str = data.get("active", "default") + if active_env_name == "default": + return get_defaults()["ask_confirmation"].lower() in ["true", "yes", "1"] + confirmation_value = get_value("ask_confirmation", active_env_name) if confirmation_value.lower() in ["true", "yes", "1"]: return True From 16ca822e89786908fb66c9f7ebb8d30f09f50e7b Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 26 Jun 2025 16:55:47 +0300 Subject: [PATCH 64/64] refactoring --- multiversx_sdk_cli/config_env.py | 39 ++++++++++++-------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/multiversx_sdk_cli/config_env.py b/multiversx_sdk_cli/config_env.py index bdbb239d..8bf5d1ac 100644 --- a/multiversx_sdk_cli/config_env.py +++ b/multiversx_sdk_cli/config_env.py @@ -44,18 +44,23 @@ def get_defaults() -> dict[str, str]: } +def _get_env_value(key: str) -> str: + """Returns the value of a key for the active environment.""" + data = read_env_file() + active_env_name: str = data.get("active", "default") + + if active_env_name == "default": + return get_defaults()[key] + return get_value(key, active_env_name) + + @cache def get_address_hrp() -> str: """ Returns the HRP for the active environment. If not set, it returns the default value. """ - data = read_env_file() - active_env_name: str = data.get("active", "default") - - if active_env_name == "default": - return get_defaults()["default_address_hrp"] - return get_value("default_address_hrp", active_env_name) + return _get_env_value("default_address_hrp") @cache @@ -64,12 +69,7 @@ def get_proxy_url() -> str: Returns the proxy URL for the active environment. If not set, it returns an empty string. """ - data = read_env_file() - active_env_name: str = data.get("active", "default") - - if active_env_name == "default": - return get_defaults()["proxy_url"] - return get_value("proxy_url", active_env_name) + return _get_env_value("proxy_url") @cache @@ -78,12 +78,7 @@ def get_explorer_url() -> str: Returns the explorer URL for the active environment. If not set, it returns an empty string. """ - data = read_env_file() - active_env_name: str = data.get("active", "default") - - if active_env_name == "default": - return get_defaults()["explorer_url"] - return get_value("explorer_url", active_env_name) + return _get_env_value("explorer_url") @cache @@ -92,13 +87,7 @@ def get_confirmation_setting() -> bool: Returns the confirmation setting for the active environment. If not set, it defaults to False. """ - data = read_env_file() - active_env_name: str = data.get("active", "default") - - if active_env_name == "default": - return get_defaults()["ask_confirmation"].lower() in ["true", "yes", "1"] - - confirmation_value = get_value("ask_confirmation", active_env_name) + confirmation_value = _get_env_value("ask_confirmation") if confirmation_value.lower() in ["true", "yes", "1"]: return True elif confirmation_value.lower() in ["false", "no", "0"]: