diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index fda48f40..0fa4d9bb 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 }} @@ -16,7 +19,7 @@ jobs: strategy: matrix: - os: [windows-2019] + os: [windows-2022] python-version: [3.11] steps: 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 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 diff --git a/CLI.md b/CLI.md index 79de3032..e27952f1 100644 --- a/CLI.md +++ b/CLI.md @@ -23,16 +23,19 @@ See: COMMAND GROUPS: - {contract,tx,validator,ledger,wallet,validator-wallet,deps,config,localnet,data,staking-provider,dns,faucet} + {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 -v, --version show program's version number and exit --verbose + --log-level {debug,info,warning,error} + default: info ---------------------- COMMAND GROUPS summary ---------------------- +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 @@ -40,12 +43,16 @@ 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 +multisig Deploy and interact with the Multisig Smart Contract +governance Propose, vote and interact with the governance contract. +config-env Configure MultiversX CLI to use specific environment values. +get Get info from the network. ``` ## Group **Contract** @@ -122,10 +129,11 @@ 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 - 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) @@ -134,8 +142,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) @@ -158,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) @@ -216,10 +222,11 @@ 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 - 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) @@ -228,8 +235,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) @@ -257,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) @@ -320,10 +325,11 @@ 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 - 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) @@ -332,8 +338,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) @@ -356,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) @@ -412,10 +416,40 @@ 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 - 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) + --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.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) @@ -501,10 +535,11 @@ 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 - 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) @@ -512,8 +547,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) @@ -537,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) @@ -608,10 +641,11 @@ 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 - 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) @@ -626,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) @@ -666,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) @@ -723,10 +757,11 @@ 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 - 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) @@ -734,8 +769,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) @@ -751,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) @@ -780,10 +813,11 @@ 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 - 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) @@ -791,8 +825,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) @@ -808,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) @@ -835,10 +867,11 @@ 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 - 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) @@ -846,8 +879,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) @@ -863,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) @@ -890,10 +921,11 @@ 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 - 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) @@ -901,8 +933,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) @@ -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) @@ -945,10 +975,11 @@ 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 - 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) @@ -956,8 +987,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,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) @@ -1000,10 +1029,11 @@ 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 - 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) @@ -1011,8 +1041,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) @@ -1028,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) @@ -1054,10 +1082,11 @@ 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 - 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) @@ -1065,8 +1094,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) @@ -1082,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,10 +1136,11 @@ 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 - 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) @@ -1120,8 +1148,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) @@ -1137,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) @@ -1164,10 +1190,11 @@ 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 - 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) @@ -1175,8 +1202,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) @@ -1192,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) @@ -1219,10 +1244,11 @@ 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 - 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) @@ -1230,8 +1256,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) @@ -1247,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) @@ -1274,10 +1298,11 @@ 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 - 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) @@ -1285,8 +1310,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) @@ -1302,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) @@ -1328,10 +1351,11 @@ 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 - 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) @@ -1339,8 +1363,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) @@ -1356,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) @@ -1422,10 +1444,11 @@ 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 - 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) @@ -1433,8 +1456,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) @@ -1450,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) @@ -1495,10 +1516,11 @@ 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 - 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) @@ -1506,8 +1528,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) @@ -1523,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) @@ -1552,10 +1572,11 @@ 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 - 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) @@ -1563,8 +1584,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) @@ -1580,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) @@ -1609,10 +1628,11 @@ 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 - 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) @@ -1620,8 +1640,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) @@ -1637,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) @@ -1666,10 +1684,11 @@ 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 - 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) @@ -1677,8 +1696,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) @@ -1694,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) @@ -1723,10 +1740,11 @@ 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 - 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) @@ -1734,8 +1752,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) @@ -1751,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) @@ -1780,10 +1796,11 @@ 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 - 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) @@ -1791,8 +1808,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) @@ -1808,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) @@ -1835,10 +1850,11 @@ 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 - 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) @@ -1846,8 +1862,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) @@ -1863,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) @@ -1890,10 +1904,11 @@ 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 - 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,8 +1916,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) @@ -1918,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) @@ -1945,10 +1958,11 @@ 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 - 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) @@ -1956,8 +1970,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) @@ -1973,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) @@ -2000,10 +2012,11 @@ 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 - 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) @@ -2011,8 +2024,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) @@ -2028,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) @@ -2055,10 +2066,11 @@ 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 - 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) @@ -2066,8 +2078,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) @@ -2083,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) @@ -2111,10 +2121,11 @@ 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 - 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) @@ -2122,8 +2133,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) @@ -2139,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) @@ -2167,10 +2176,11 @@ 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 - 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) @@ -2178,8 +2188,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) @@ -2195,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) @@ -2224,10 +2232,11 @@ 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 - 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) @@ -2235,8 +2244,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) @@ -2252,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) @@ -2281,10 +2288,11 @@ 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 - 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) @@ -2292,8 +2300,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) @@ -2309,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) @@ -2339,10 +2345,11 @@ 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 - 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) @@ -2350,8 +2357,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) @@ -2367,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) @@ -2397,10 +2402,11 @@ 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 - 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) @@ -2408,8 +2414,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) @@ -2425,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) @@ -2539,10 +2543,11 @@ 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 - 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) @@ -2830,7 +2835,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 +2846,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 +2863,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 +2877,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 +2893,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 +2910,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 +2927,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 @@ -3060,10 +3065,11 @@ 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 - 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) @@ -3074,3 +3080,2978 @@ options: --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" +} + +options: + -h, --help show this help message and exit + --bytecode BYTECODE the file containing the WASM bytecode + --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) + --abi ABI the ABI file of the Multisig 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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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) + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +options: + -h, --help show this help message and exit + --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) + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +options: + -h, --help show this help message and exit + --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, + 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 }] + --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) + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --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 + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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, + 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 }] + --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) + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --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 + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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, + 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 }] + --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) + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --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 + --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 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 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) + --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 + + +``` +$ 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" +} + +options: + -h, --help show this help message and exit + --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 }] + --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) + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +options: + -h, --help show this help message and exit + --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 }] + --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) + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +options: + -h, --help show this help message and exit + --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) + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ 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" +} + +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 + --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 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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --gas-price GAS_PRICE ⛽ the gas price (default: 1000000000) + --gas-limit GAS_LIMIT ⛽ the gas limit + --value VALUE the value to transfer (default: 0) + --chain CHAIN the chain identifier + --version VERSION the transaction version (default: 2) + --options OPTIONS the transaction options (default: 0) + --relayer RELAYER the bech32 address of the relayer + --guardian GUARDIAN the bech32 address of the guardian + --send ✓ whether to broadcast the transaction (default: False) + --simulate whether to simulate the transaction (default: False) + --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 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 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) + --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 + + +``` +$ mxpy multisig get-quorum --help +usage: mxpy multisig get-quorum [-h] ... + +Perform a smart contract query to get the quorum. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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. + +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 + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --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 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 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) + --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 + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --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 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 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) + --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 + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --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 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 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) + --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...) + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --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 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 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) + --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 + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --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 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 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) + --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 + --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 + --proxy PROXY 🔗 the URL of the proxy + --nonce NONCE # the nonce for the transaction. If not provided, is fetched from the + network. + --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 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 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) + --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 + +``` +## Group **ConfigEnv** + + +``` +$ mxpy config-env --help +usage: mxpy config-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 specified environment. +set Sets an env value for the specified environment. +dump Dumps 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. Use `mxpy config-env switch` to move to another env. +reset Deletes the environment file. Default env will be used. + +``` +### ConfigEnv.New + + +``` +$ mxpy config-env new --help +usage: mxpy config-env new [-h] ... + +Creates a new environment and sets it as the active environment. + +positional arguments: + name the name of the new environment + +options: + -h, --help show this help message and exit + --template TEMPLATE an environment from which to create the new environment + +``` +### ConfigEnv.Set + + +``` +$ mxpy config-env set --help +usage: mxpy config-env set [-h] ... + +Sets an env value for the specified environment. + +positional arguments: + name the name of the configuration entry + value the new value + +options: + -h, --help show this help message and exit + --env ENV the name of the environment to operate on + +``` +### ConfigEnv.Get + + +``` +$ mxpy config-env get --help +usage: mxpy config-env get [-h] ... + +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 + + +``` +$ mxpy config-env dump --help +usage: mxpy config-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. + +``` +### ConfigEnv.Switch + + +``` +$ mxpy config-env switch --help +usage: mxpy config-env switch [-h] ... + +Switch to a different environment. + +options: + -h, --help show this help message and exit + --env ENV the name of the environment to operate on + +``` +### ConfigEnv.List + + +``` +$ mxpy config-env list --help +usage: mxpy config-env list [-h] ... + +List available environments + +options: + -h, --help show this help message and exit + +``` +### ConfigEnv.Remove + + +``` +$ mxpy config-env remove --help +usage: mxpy config-env remove [-h] ... + +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 + --env ENV the name of the environment to operate on + +``` +### ConfigEnv.Reset + + +``` +$ mxpy config-env reset --help +usage: mxpy config-env reset [-h] ... + +Deletes the environment file. Default env will be used. + +options: + -h, --help show this help message and exit + +``` +## Group **ConfigWallet** + + +``` +$ mxpy config-wallet --help +usage: mxpy config-wallet 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 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. + +``` +### ConfigWallet.New + + +``` +$ mxpy config-wallet new --help +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 + +options: + -h, --help show this help message and exit + --path PATH the absolute path to the wallet file + +``` +### ConfigWallet.List + + +``` +$ mxpy config-wallet list --help +usage: mxpy config-wallet list [-h] ... + +List configured wallets + +options: + -h, --help show this help message and exit + +``` +### ConfigWallet.Dump + + +``` +$ mxpy config-wallet dump --help +usage: mxpy config-wallet dump [-h] ... + +Dumps the active wallet. + +options: + -h, --help show this help message and exit + +``` +### ConfigWallet.Get + + +``` +$ mxpy config-wallet get --help +usage: mxpy config-wallet get [-h] ... + +Gets a config value from the specified wallet. + +positional arguments: + value the value to get from the specified wallet (e.g. path) + +options: + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet + +``` +### ConfigWallet.Set + + +``` +$ mxpy config-wallet set --help +usage: mxpy config-wallet set [-h] ... + +Sets a config value for the specified wallet. + +positional arguments: + 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 + --alias ALIAS the alias of the wallet + +``` +### ConfigWallet.Switch + + +``` +$ mxpy config-wallet switch --help +usage: mxpy config-wallet switch [-h] ... + +Switch to a different wallet. + +options: + -h, --help show this help message and exit + --alias ALIAS the alias of the wallet + +``` +### ConfigWallet.Delete + + +``` +$ mxpy config-wallet delete --help +usage: mxpy config-wallet delete [-h] ... + +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 + --alias ALIAS the alias of the wallet + +``` +### ConfigWallet.Remove + + +``` +$ mxpy config-wallet remove --help +usage: mxpy config-wallet remove [-h] ... + +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 + --alias ALIAS the alias of the wallet + +``` +### ConfigWallet.Reset + + +``` +$ mxpy config-wallet reset --help +usage: mxpy config-wallet reset [-h] ... + +Deletes the config file. No default wallet 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,storage,storage-entry,token,transaction,network-config,network-status} + +OPTIONS: + -h, --help show this help message and exit + +---------------- +COMMANDS summary +---------------- +account Get info about 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 + + +``` +$ 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.Storage + + +``` +$ mxpy get storage --help +usage: mxpy get storage [-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 46ebfb16..9c7dc806 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" @@ -130,6 +131,87 @@ 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" + + 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" + 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.Switch" "config-wallet switch" + command "ConfigWallet.Delete" "config-wallet delete" + command "ConfigWallet.Remove" "config-wallet remove" + command "ConfigWallet.Reset" "config-wallet reset" + + group "Get" "get" + command "Get.Account" "get account" + command "Get.Storage" "get storage" + command "Get.StorageEntry" "get storage-entry" + command "Get.Token" "get token" + command "Get.Transaction" "get transaction" } generate 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/base_transactions_controller.py b/multiversx_sdk_cli/base_transactions_controller.py index 858d81ca..8db775a6 100644 --- a/multiversx_sdk_cli/base_transactions_controller.py +++ b/multiversx_sdk_cli/base_transactions_controller.py @@ -1,14 +1,33 @@ -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_env 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, TransactionSigningError +from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount +logger = logging.getLogger("base_controller") + class BaseTransactionsController: def __init__(self) -> None: @@ -28,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, @@ -75,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) @@ -83,4 +108,65 @@ 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] = [] + + 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 + + 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/cli.py b/multiversx_sdk_cli/cli.py index 00519941..8ad91fd5 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -10,20 +10,28 @@ 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_faucet +import multiversx_sdk_cli.cli_get +import multiversx_sdk_cli.cli_governance 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 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.config_env import get_address_hrp +from multiversx_sdk_cli.constants import LOG_LEVELS, SDK_PATH logger = logging.getLogger("cli") @@ -42,11 +50,12 @@ 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) + utils.ensure_folder(SDK_PATH) + parser = setup_parser(cli_args) argcomplete.autocomplete(parser) - args = parser.parse_args(argv_with_config_args) + + _handle_global_arguments(cli_args) + args = parser.parse_args(cli_args) if args.verbose: logging.basicConfig( @@ -56,22 +65,21 @@ 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)], ) 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: - 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: + set_proxy_from_config_if_not_provided(args) args.func(args) @@ -105,10 +113,18 @@ 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=LOG_LEVELS, + help="default: %(default)s", + ) subparsers = parser.add_subparsers() commands: list[Any] = [] + 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)) @@ -122,6 +138,10 @@ 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)) + commands.append(multiversx_sdk_cli.cli_governance.setup_parser(args, subparsers)) + commands.append(multiversx_sdk_cli.cli_config_env.setup_parser(subparsers)) + commands.append(multiversx_sdk_cli.cli_get.setup_parser(subparsers)) parser.epilog = """ ---------------------- @@ -147,6 +167,26 @@ def verify_deprecated_entries_in_config_file(): ux.show_warning(message.rstrip("\n")) +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:]) sys.exit(ret) 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/cli_config_env.py b/multiversx_sdk_cli/cli_config_env.py new file mode 100644 index 00000000..2c4282d8 --- /dev/null +++ b/multiversx_sdk_cli/cli_config_env.py @@ -0,0 +1,160 @@ +import logging +import os +from typing import Any + +from multiversx_sdk_cli import cli_shared +from multiversx_sdk_cli.config_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.config_env") + + +def setup_parser(subparsers: Any) -> Any: + parser = cli_shared.add_group_subparser( + subparsers, "config-env", "Configure MultiversX CLI to use specific environment values." + ) + subparsers = parser.add_subparsers() + + sub = cli_shared.add_command_subparser( + subparsers, "config-env", "new", "Creates a new environment and sets it as the active environment." + ) + sub.add_argument("name", type=str, help="the name of the new environment") + 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, "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 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.") + 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, "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_env_arg(sub) + sub.set_defaults(func=switch_env) + + 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, + "config-env", + "remove", + "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) + + sub = cli_shared.add_command_subparser( + subparsers, + "config-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 _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()) + else: + dump_out_json(get_active_env()) + + +def get_env_value(args: Any): + value = get_value(args.name, args.env) + print(value) + + +def set_env_value(args: Any): + set_value(args.name, args.value, args.env) + + +def delete_env_value(args: Any): + delete_value(args.name, args.env) + + +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.env) + 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.env) + + +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_config_wallet.py b/multiversx_sdk_cli/cli_config_wallet.py new file mode 100644 index 00000000..08c90dc0 --- /dev/null +++ b/multiversx_sdk_cli/cli_config_wallet.py @@ -0,0 +1,155 @@ +import logging +import os +from typing import Any + +from multiversx_sdk_cli import cli_shared +from multiversx_sdk_cli.config_wallet import ( + create_new_wallet_config, + delete_alias, + delete_config_value, + get_active_wallet, + get_value, + read_wallet_config_file, + resolve_wallet_config_path, + set_value, + switch_wallet, +) +from multiversx_sdk_cli.utils import dump_out_json +from multiversx_sdk_cli.ux import confirm_continuation + +logger = logging.getLogger("cli.config_wallet") + + +def setup_parser(subparsers: Any) -> Any: + 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, "config-wallet", "new", "Creates a new wallet config and sets it as the active wallet." + ) + 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.set_defaults(func=new_wallet_config) + + sub = cli_shared.add_command_subparser(subparsers, "config-wallet", "list", "List configured wallets") + 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) + + 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_wallet_config_value) + + 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_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_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_wallet_to_active) + + sub = cli_shared.add_command_subparser( + subparsers, + "config-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.", + ) + _add_alias_arg(sub) + sub.set_defaults(func=remove_wallet) + + sub = cli_shared.add_command_subparser( + subparsers, + "config-wallet", + "reset", + "Deletes the config file. No default wallet will be set.", + ) + sub.set_defaults(func=delete_wallet_config_file) + + parser.epilog = cli_shared.build_group_epilog(subparsers) + return subparsers + + +def _add_alias_arg(sub: Any): + sub.add_argument("--alias", type=str, required=True, help="the alias of the wallet") + + +def new_wallet_config(args: Any): + create_new_wallet_config(name=args.alias, path=args.path) + dump_out_json(get_active_wallet()) + + +def list_wallets(args: Any): + _ensure_wallet_config_file_exists() + + data = read_wallet_config_file() + dump_out_json(data) + + +def dump(args: Any): + _ensure_wallet_config_file_exists() + dump_out_json(get_active_wallet()) + + +def get_wallet_config_value(args: Any): + _ensure_wallet_config_file_exists() + value = get_value(args.value, args.alias) + print(value) + + +def set_wallet_config_value(args: Any): + _ensure_wallet_config_file_exists() + set_value(args.key, args.value, args.alias) + + +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_wallet_to_active(args: Any): + _ensure_wallet_config_file_exists() + + switch_wallet(args.alias) + dump_out_json(get_active_wallet()) + + +def remove_wallet(args: Any): + _ensure_wallet_config_file_exists() + delete_alias(args.alias) + + +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...") + 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_wallet_config_file_exists(): + address_file = resolve_wallet_config_path() + if not address_file.is_file(): + logger.info("Wallet config file not found. Aborting...") + exit(1) diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 7ad92e1f..c79ee17e 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, @@ -26,6 +25,7 @@ ) 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 @@ -56,23 +56,13 @@ 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) 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) @@ -94,17 +84,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) @@ -121,22 +101,12 @@ 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) _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) @@ -336,40 +306,12 @@ 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. 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) @@ -377,7 +319,6 @@ def deploy(args: Any): logger.debug("deploy") validate_transaction_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) @@ -387,7 +328,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 @@ -405,6 +346,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), @@ -416,7 +358,9 @@ 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()) + + 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) @@ -425,7 +369,6 @@ def call(args: Any): logger.debug("call") validate_transaction_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) @@ -435,7 +378,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 @@ -444,6 +387,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, @@ -451,8 +398,9 @@ 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, + token_transfers=token_transfers, nonce=sender.nonce, version=int(args.version), options=int(args.options), @@ -466,7 +414,6 @@ def upgrade(args: Any): logger.debug("upgrade") validate_transaction_args(args) - ensure_wallet_args_are_provided(args) validate_broadcast_args(args) validate_chain_id_args(args) @@ -476,7 +423,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 @@ -496,6 +443,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/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index ab7e27bb..cb285781 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, @@ -389,13 +388,12 @@ 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) 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_get.py b/multiversx_sdk_cli/cli_get.py new file mode 100644 index 00000000..45bd6d3a --- /dev/null +++ b/multiversx_sdk_cli/cli_get.py @@ -0,0 +1,232 @@ +import logging +from typing import Any, Optional + +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.config import get_config_for_network_providers +from multiversx_sdk_cli.config_env import MxpyEnv +from multiversx_sdk_cli.errors import ( + ArgumentsNotProvidedError, + BadUsage, + NetworkProviderError, +) +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", "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_storage) + + 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) + + 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 + + +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_storage(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()}") + 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) + + +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) + 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_network_config(args: Any): + proxy = _get_proxy(args) + config = proxy.get_network_config() + + dump_out_json(config.raw) + + +def get_network_status(args: Any): + proxy = _get_proxy(args) + status = proxy.get_network_status() + + dump_out_json(status.raw) + + +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 + + +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_governance.py b/multiversx_sdk_cli/cli_governance.py new file mode 100644 index 00000000..c9ebbbcf --- /dev/null +++ b/multiversx_sdk_cli/cli_governance.py @@ -0,0 +1,449 @@ +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 ( + 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") + _add_common_args(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", + ) + _add_common_args(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) + _add_common_args(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...)", + ) + _add_common_args(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()}", + ) + _add_common_args(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") + _add_common_args(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 _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 _ensure_args(args: Any): + validate_broadcast_args(args) + validate_chain_id_args(args) + + +def create_proposal(args: Any): + _ensure_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.proxy, args.chain) + 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_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.proxy, args.chain) + 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_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.proxy, args.chain) + 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_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.proxy, args.chain) + 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_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.proxy, args.chain) + 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_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.proxy, args.chain) + 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_multisig.py b/multiversx_sdk_cli/cli_multisig.py new file mode 100644 index 00000000..149fce85 --- /dev/null +++ b/multiversx_sdk_cli/cli_multisig.py @@ -0,0 +1,1866 @@ +import json +import logging +from pathlib import Path +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, + Transaction, + TransactionsFactoryConfig, +) +from multiversx_sdk.abi import Abi + +from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.args_validation import ( + validate_broadcast_args, + validate_chain_id_args, + validate_proxy_argument, + 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 + +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() + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "deploy", + f"Deploy a Multisig Smart Contract.{output_description}", + ) + _add_bytecode_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", + ) + cli_shared.add_metadata_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=False, with_receiver_arg=False) + + 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}", + ) + cli_shared.add_token_transfers_args(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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_action_id_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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}", + ) + sub.add_argument( + "--action-ids", + required=True, + nargs="+", + type=int, + help="the IDs of the actions to discard", + ) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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}", + ) + sub.add_argument( + "--board-member", + required=True, + type=str, + help="the bech32 address of the proposed board member", + ) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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}", + ) + sub.add_argument( + "--proposer", + required=True, + type=str, + help="the bech32 address of the proposed proposer", + ) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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}", + ) + sub.add_argument( + "--user", + required=True, + type=str, + help="the bech32 address of the proposed user to be removed", + ) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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}", + ) + sub.add_argument( + "--quorum", + required=True, + type=int, + help="the size of the new quorum (number of signatures required to approve a proposal)", + ) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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}", + ) + sub.add_argument( + "--opt-gas-limit", + type=int, + 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") + _add_arguments_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=True) + + 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}", + ) + cli_shared.add_token_transfers_args(sub) + sub.add_argument( + "--opt-gas-limit", + type=int, + 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") + _add_arguments_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=True) + + 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}", + ) + cli_shared.add_token_transfers_args(sub) + sub.add_argument( + "--opt-gas-limit", + type=int, + 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") + _add_arguments_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=True) + + 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}", + ) + + 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") + cli_shared.add_metadata_arg(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) + + 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}", + ) + 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") + cli_shared.add_metadata_arg(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) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "sign-action", + f"Sign a proposed action.{output_description}", + ) + _add_action_id_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + sub.set_defaults(func=sign_action) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "sign-batch", + f"Sign a batch of actions.{output_description}", + ) + sub.add_argument("--batch", required=True, type=int, help="the id of the batch to sign") + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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_action_id_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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}", + ) + sub.add_argument("--batch", required=True, type=int, help="the id of the batch to sign") + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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_action_id_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + sub.set_defaults(func=unsign_action) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "unsign-batch", + f"Unsign a batch of actions.{output_description}", + ) + 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) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "unsign-for-outdated-members", + f"Unsign an action for outdated board members.{output_description}", + ) + _add_action_id_arg(sub) + sub.add_argument( + "--outdated-members", + nargs="+", + type=int, + help="IDs of the outdated board members", + ) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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_action_id_arg(sub) + _add_common_args(args=args, sub=sub, with_contract_arg=True, with_receiver_arg=False) + + 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}", + ) + 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) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-quorum", + "Perform a smart contract query to get the quorum.", + ) + _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", + "Perform a smart contract query to get the number of board members.", + ) + _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", + "Perform a smart contract query to get the number of groups.", + ) + _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", + "Perform a smart contract query to get the number of proposers.", + ) + _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", + "Perform a smart contract query to get the actions in a group.", + ) + _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", + "Perform a smart contract query to get the id of the last action in a group.", + ) + _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", + "Perform a smart contract query to get the index of the last action.", + ) + _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", + "Perform a smart contract query to check if an action is signed by a user.", + ) + _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", + "Perform a smart contract query to check if an action has reached quorum.", + ) + _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", + "Perform a smart contract query to get the pending actions full info.", + ) + _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", + "Perform a smart contract query to get the role of a user.", + ) + _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", + "Perform a smart contract query to get all the board members.", + ) + _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", + "Perform a smart contract query to get all the proposers.", + ) + _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", + "Perform a smart contract query to get the data of an action.", + ) + _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", + "Perform a smart contract query to get the signers of an action.", + ) + _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_signers) + + sub = cli_shared.add_command_subparser( + subparsers, + "multisig", + "get-action-signers-count", + "Perform a smart contract query to get the number of signers of an action.", + ) + _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", + "Perform a smart contract query to get the number of valid signers of an action.", + ) + _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", + "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") + 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_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 _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) + cli_shared.add_wait_result_and_timeout_args(sub) + + +def _ensure_args(args: Any): + validate_transaction_args(args) + validate_broadcast_args(args) + validate_chain_id_args(args) + + +def deploy(args: Any): + logger.debug("multisig.deploy") + _ensure_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.proxy, args.chain) + 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): + _ensure_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.proxy, args.chain) + 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 = cli_shared.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): + _ensure_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.proxy, args.chain) + 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): + _ensure_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.proxy, args.chain) + 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): + _ensure_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.proxy, args.chain) + 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): + _ensure_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.proxy, args.chain) + 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): + _ensure_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.proxy, args.chain) + 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): + _ensure_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.proxy, args.chain) + 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): + _ensure_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.proxy, args.chain) + 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_for_factory=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): + _ensure_args(args) + + 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(), + args=args, + ) + + abi = Abi.load(Path(args.abi)) + chain_id = cli_shared.get_chain_id(args.proxy, args.chain) + 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 = cli_shared.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_for_factory=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): + _ensure_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.proxy, args.chain) + 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 = args.token_transfers or None + if token_transfers: + token_transfers = cli_shared.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_for_factory=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): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + 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, + 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_for_factory=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 upgrade_from_source(args: Any): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + contract_to_upgrade = Address.new_from_bech32(args.contract_to_upgrade) + 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_upgrade_from_source_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + 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_for_factory=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 sign_action(args: Any): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) + + tx = multisig.prepare_sign_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 sign_batch(args: Any): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + batch_id = int(args.batch) + + tx = multisig.prepare_sign_batch_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + batch_id=batch_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 sign_and_perform(args: Any): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) + + tx = multisig.prepare_sign_and_perform_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 sign_batch_and_perform(args: Any): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + batch_id = int(args.batch) + + tx = multisig.prepare_sign_batch_and_perform_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + batch_id=batch_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 unsign_action(args: Any): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) + + tx = multisig.prepare_unsign_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 unsign_batch(args: Any): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + batch_id = int(args.batch) + + tx = multisig.prepare_unsign_batch_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + batch_id=batch_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 unsign_for_outdated_board_members(args: Any): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) + + tx = multisig.prepare_unsign_for_outdated_board_members_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + 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), + guardian_and_relayer_data=guardian_and_relayer_data, + ) + + _send_or_simulate(tx, contract, args) + + +def perform_action(args: Any): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + action_id = int(args.action) + + tx = multisig.prepare_perform_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 perform_batch(args: Any): + _ensure_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.proxy, args.chain) + multisig = MultisigWrapper(TransactionsFactoryConfig(chain_id), abi) + + contract = Address.new_from_bech32(args.contract) + batch_id = int(args.batch) + + tx = multisig.prepare_perform_batch_transaction( + owner=sender, + nonce=sender.nonce, + contract=contract, + batch_id=batch_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 get_quorum(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + 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) + + 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)) + 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) + + 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)) + 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) + + 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)) + 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) + + 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)) + 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) + + 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)) + 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) + + 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)) + 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) + + 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)) + 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) + 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)) + 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) + 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)) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) + chain_id = proxy.get_network_config().chain_id + contract = Address.new_from_bech32(args.contract) + + controller = MultisigController(chain_id, proxy, abi) + actions = controller.get_pending_actions_full_info(contract) + + output = [_convert_action_full_info_to_dict(action) for action in actions] + utils.dump_out_json(output) + + +def get_user_role(args: Any): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + 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) + 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)) + 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) + 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)) + 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) + if not proposers: + print(None) + else: + 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)) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) + chain_id = proxy.get_network_config().chain_id + controller = MultisigController(chain_id, proxy, abi) + + contract = Address.new_from_bech32(args.contract) + action = args.action + + 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): + validate_proxy_argument(args) + + abi = Abi.load(Path(args.abi)) + 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) + 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)) + 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) + 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)) + 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) + 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)) + 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) + + id = multisig.await_completed_execute_propose_any(args.hash) + print(f"Proposal ID: {id}") + + +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 _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], + } diff --git a/multiversx_sdk_cli/cli_password.py b/multiversx_sdk_cli/cli_password.py index 98666c31..80535216 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. 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() 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. 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() 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. 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() return getpass("Keyfile's password: ") diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 316b8629..959b8e0e 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -1,9 +1,13 @@ import argparse import ast +import base64 +import logging import sys from argparse import FileType +from functools import cache +from getpass import getpass from pathlib import Path -from typing import Any, Text, Union, cast +from typing import Any, Optional, Text, Union, cast from multiversx_sdk import ( Account, @@ -11,32 +15,49 @@ ApiNetworkProvider, LedgerAccount, ProxyNetworkProvider, + Token, + TokenComputer, + TokenTransfer, Transaction, ) -from multiversx_sdk_cli import config, errors, utils +from multiversx_sdk_cli import config, utils 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_env import MxpyEnv, get_address_hrp +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, TCS_SERVICE_ID, ) from multiversx_sdk_cli.errors import ( + AddressConfigFileError, ArgumentsNotProvidedError, BadUsage, IncorrectWalletError, + LedgerError, + NoWalletProvided, + UnknownWalletAliasError, + WalletError, ) from multiversx_sdk_cli.guardian_relayer_data import GuardianRelayerData from multiversx_sdk_cli.interfaces import IAccount from multiversx_sdk_cli.simulation import Simulator from multiversx_sdk_cli.transactions import send_and_wait_for_result 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 + +logger = logging.getLogger("cli_shared") + trusted_cosigner_service_url_by_chain_id = { "1": "https://tools.multiversx.com/guardian", @@ -106,12 +127,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") @@ -125,7 +140,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( @@ -148,6 +163,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, @@ -160,7 +176,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="DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter the password.", ) sub.add_argument( "--ledger", @@ -204,7 +220,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="DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter the password.", ) sub.add_argument( "--guardian-ledger", @@ -225,7 +241,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="DEPRECATED, do not use it anymore. Instead, you'll be prompted to enter the password.", ) sub.add_argument( "--relayer-ledger", @@ -279,6 +295,48 @@ 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 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) @@ -294,16 +352,71 @@ 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: - 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)) + elif args.sender: + return load_wallet_by_alias(alias=args.sender, hrp=hrp) + else: + return load_default_wallet(hrp=hrp) + + +def load_wallet_by_alias(alias: str, hrp: str) -> Account: + file_path = resolve_wallet_config_path() + if not file_path.is_file(): + raise AddressConfigFileError("The wallet config file was not found.") + + file = read_wallet_config_file() + if file == dict(): + raise AddressConfigFileError("Wallet config file is empty.") + + wallets: dict[str, Any] = file["wallets"] + wallet = wallets.get(alias, None) + if not wallet: + raise UnknownWalletAliasError(alias) + + logger.info(f"Using sender [{alias}] from wallet config.") + return _load_wallet_from_wallet_config(wallet=wallet, hrp=hrp) + + +def load_default_wallet(hrp: str) -> Account: + 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_wallet_config_file().get("active", "") + logger.info(f"Using sender [{alias_of_default_wallet}] from wallet config.") + + return _load_wallet_from_wallet_config(wallet=active_wallet, hrp=hrp) + + +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.") + path = Path(wallet_path) + + index = int(wallet.get("index", 0)) + + if path.suffix == ".pem": + return Account.new_from_pem(file_path=path, index=index, hrp=hrp) + elif path.suffix == ".json": + logger.info(f"Using keystore wallet at: [{path}].") + password = getpass("Please enter the wallet password: ") + + try: + 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 errors.NoWalletProvided() + raise WalletError(f"Unsupported wallet file type: [{path.suffix}]. Supported types are: `.pem` and `.json`.") def _get_address_hrp(args: Any) -> str: @@ -322,7 +435,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: @@ -350,14 +463,20 @@ 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: - 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 @@ -481,14 +600,20 @@ 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: - 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 @@ -502,22 +627,16 @@ def get_current_nonce_for_address(address: Address, proxy_url: Union[str, None]) return proxy.get_account(address).nonce -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 - +@cache +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 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) @@ -557,11 +676,16 @@ 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 + hash = b"" try: if send_wait_result: + _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(tx) + hash = proxy.send_transaction(tx) output_builder.set_emitted_transaction_hash(hash.hex()) elif simulate: @@ -573,15 +697,31 @@ 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: + cli_config = MxpyEnv.from_active_env() log_explorer_transaction( chain=output_transaction["emittedTransaction"]["chainID"], transaction_hash=output_transaction["emittedTransactionHash"], + explorer_url=cli_config.explorer_url, ) return output_builder +def _confirm_continuation_if_required(tx: Transaction) -> None: + env = MxpyEnv.from_active_env() + + if env.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 +757,36 @@ 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): + extended_identifier = transfers[i] + amount = int(transfers[i + 1]) + 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) + token_transfers.append(transfer) + + return token_transfers + + +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 + + 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 27785f43..7e2038e1 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -2,19 +2,11 @@ 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 ( ensure_relayer_wallet_args_are_provided, - ensure_wallet_args_are_provided, validate_broadcast_args, validate_chain_id_args, validate_nonce_args, @@ -51,17 +43,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( @@ -116,7 +98,6 @@ def _add_common_arguments(args: list[str], sub: Any): 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) @@ -133,9 +114,9 @@ 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) + chain_id = cli_shared.get_chain_id(args.proxy, args.chain) tx_controller = TransactionsController(chain_id) tx = tx_controller.create_transaction( @@ -155,22 +136,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) @@ -181,6 +146,8 @@ def send_transaction(args: Any): proxy = ProxyNetworkProvider(url=args.proxy, config=config) try: + cli_shared._confirm_continuation_if_required(tx) + tx_hash = proxy.send_transaction(tx) output.set_emitted_transaction_hash(tx_hash.hex()) finally: @@ -195,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/cli_validators.py b/multiversx_sdk_cli/cli_validators.py index 7d06feb9..51764bd2 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, @@ -148,7 +147,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) @@ -197,7 +195,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 @@ -219,6 +217,7 @@ def _parse_public_bls_keys(public_bls_keys: str) -> list[ValidatorPublicKey]: def do_unstake(args: Any): validate_args(args) + sender = cli_shared.prepare_sender(args) guardian_and_relayer_data = cli_shared.get_guardian_and_relayer_data( sender=sender.address.to_bech32(), @@ -247,6 +246,7 @@ def do_unstake(args: Any): def do_unjail(args: Any): 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(), @@ -275,6 +275,7 @@ def do_unjail(args: Any): def do_unbond(args: Any): 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(), @@ -303,6 +304,7 @@ def do_unbond(args: Any): def change_reward_address(args: Any): 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(), @@ -331,6 +333,7 @@ def change_reward_address(args: Any): def do_claim(args: Any): 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(), @@ -357,6 +360,7 @@ def do_claim(args: Any): def do_unstake_nodes(args: Any): 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(), @@ -386,6 +390,7 @@ def do_unstake_nodes(args: Any): def do_unstake_tokens(args: Any): 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(), @@ -414,6 +419,7 @@ def do_unstake_tokens(args: Any): def do_unbond_nodes(args: Any): 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(), @@ -443,6 +449,7 @@ def do_unbond_nodes(args: Any): def do_unbond_tokens(args: Any): 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(), @@ -471,6 +478,7 @@ def do_unbond_tokens(args: Any): def do_clean_registered_data(args: Any): 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(), @@ -497,6 +505,7 @@ def do_clean_registered_data(args: Any): def do_restake_unstaked_nodes(args: Any): 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(), diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 558e720a..488d814f 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -9,7 +9,7 @@ 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.config_env import get_address_hrp from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS from multiversx_sdk_cli.errors import ( BadUsage, @@ -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/config.py b/multiversx_sdk_cli/config.py index ee83f3c5..9b6c89b5 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -1,33 +1,18 @@ import os +from functools import cache from pathlib import Path from typing import Any from multiversx_sdk import NetworkProviderConfig from multiversx_sdk_cli import errors, utils +from multiversx_sdk_cli.constants import LOG_LEVELS, SDK_PATH +from multiversx_sdk_cli.ux import show_warning -SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() LOCAL_CONFIG_PATH = Path("mxpy.json").resolve() 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") @@ -48,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() @@ -57,10 +43,6 @@ def get_value(name: str) -> str: return value -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() @@ -145,28 +127,30 @@ 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", + "log_level": "info", } +def get_log_level_from_config() -> str: + log_level = get_value("log_level") + if log_level not in LOG_LEVELS: + 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 + + return log_level + + def get_deprecated_entries_in_config_file(): default_config_keys = set(get_defaults().keys()) current_config_keys = set(get_active().keys()) @@ -179,6 +163,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(): @@ -192,53 +177,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/config_env.py b/multiversx_sdk_cli/config_env.py new file mode 100644 index 00000000..8bf5d1ac --- /dev/null +++ b/multiversx_sdk_cli/config_env.py @@ -0,0 +1,224 @@ +from dataclasses import dataclass +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 ( + EnvironmentAlreadyExistsError, + EnvironmentProtectedError, + InvalidConfirmationSettingError, + InvalidEnvironmentValue, + UnknownEnvironmentError, +) +from multiversx_sdk_cli.utils import read_json_file, write_json_file + +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", + } + + +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. + """ + return _get_env_value("default_address_hrp") + + +@cache +def get_proxy_url() -> str: + """ + Returns the proxy URL for the active environment. + If not set, it returns an empty string. + """ + return _get_env_value("proxy_url") + + +@cache +def get_explorer_url() -> str: + """ + Returns the explorer URL for the active environment. + If not set, it returns an empty string. + """ + return _get_env_value("explorer_url") + + +@cache +def get_confirmation_setting() -> bool: + """ + Returns the confirmation setting for the active environment. + If not set, it defaults to False. + """ + 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"]: + return False + else: + raise InvalidConfirmationSettingError(confirmation_value) + + +@cache +def get_value(name: str, env_name: str) -> str: + _guard_valid_name(name) + 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 = env.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: str, env_name: str): + _guard_valid_name(name) + data = read_env_file() + + 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) + + +def write_file(data: dict[str, Any]): + env_path = resolve_env_path() + write_json_file(str(env_path), data) + + +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() + + 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) + + +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/config_wallet.py b/multiversx_sdk_cli/config_wallet.py new file mode 100644 index 00000000..71eb5745 --- /dev/null +++ b/multiversx_sdk_cli/config_wallet.py @@ -0,0 +1,177 @@ +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) + + +def _write_file(data: dict[str, Any]): + env_path = resolve_wallet_config_path() + write_json_file(str(env_path), data) + + +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) + 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): + """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 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): + wallets = env.get("wallets", {}) + if name in wallets: + 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) + + +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/constants.py b/multiversx_sdk_cli/constants.py index 9a997542..803889fe 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 @@ -19,3 +21,14 @@ 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" + +SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() + +LOG_LEVELS = ["debug", "info", "warning", "error"] diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index a944c224..c3a47597 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): @@ -70,6 +53,7 @@ def prepare_deploy_transaction( payable: bool, payable_by_sc: bool, gas_limit: int, + gas_price: int, value: int, nonce: int, version: int, @@ -78,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, @@ -94,6 +78,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,18 +101,17 @@ 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], + 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) + args = self._convert_args_to_typed_values(args) tx = self._factory.create_transaction_for_execute( sender=caller.address, @@ -136,11 +120,12 @@ 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 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 +152,7 @@ def prepare_upgrade_transaction( payable: bool, payable_by_sc: bool, gas_limit: int, + gas_price: int, value: int, nonce: int, version: int, @@ -175,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, @@ -192,6 +178,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 @@ -216,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) @@ -226,60 +213,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/delegation.py b/multiversx_sdk_cli/delegation.py index 17a8c377..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.config 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 95ab0a85..50955e34 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -10,12 +10,11 @@ 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, ) -from multiversx_sdk_cli.config import get_address_hrp +from multiversx_sdk_cli.config_env import get_address_hrp from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX from multiversx_sdk_cli.transactions import TransactionsController @@ -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) @@ -77,7 +75,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/errors.py b/multiversx_sdk_cli/errors.py index 577d6207..d03c7983 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -149,3 +149,75 @@ 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) + + +class InvalidConfirmationSettingError(KnownError): + 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}.") + + +class UnknownWalletAliasError(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) + + +class AddressConfigFileError(KnownError): + def __init__(self, message: str): + super().__init__(message) + + +class WalletError(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}") diff --git a/multiversx_sdk_cli/governance.py b/multiversx_sdk_cli/governance.py new file mode 100644 index 00000000..8042d64d --- /dev/null +++ b/multiversx_sdk_cli/governance.py @@ -0,0 +1,262 @@ +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, + ) + 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 + + 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 + ) + 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 + + 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) + 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 + + 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) + 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 + + 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) + 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 + + 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, + ) + 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 + + 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/multisig.py b/multiversx_sdk_cli/multisig.py new file mode 100644 index 00000000..be28cfa8 --- /dev/null +++ b/multiversx_sdk_cli/multisig.py @@ -0,0 +1,916 @@ +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 + +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 + +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_for_factory: 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_for_factory: + args = self._convert_args_to_typed_values(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_for_factory: 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_for_factory: + args = self._convert_args_to_typed_values(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_for_factory: 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_for_factory: + args = self._convert_args_to_typed_values(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_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_for_factory: + args = self._convert_args_to_typed_values(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_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_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_for_factory: + args = self._convert_args_to_typed_values(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 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" 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..ba806e3c --- /dev/null +++ b/multiversx_sdk_cli/tests/test_cli_default_wallet.py @@ -0,0 +1,230 @@ +import json +from pathlib import Path +from typing import Any + +from multiversx_sdk_cli import cli_shared +from multiversx_sdk_cli.cli import main + + +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.config_wallet + + 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( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--nonce", + "0", + "--chain", + "D", + ] + ) + out = _read_stdout(capsys) + assert return_code + assert "No wallet provided." in out + + # Clear the captured content + capsys.readouterr() + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--sender", + "invalidSender", + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert return_code + out = _read_stdout(capsys) + 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-wallets.mxpy.json" + assert not test_file.exists() + + import multiversx_sdk_cli.config_wallet + + 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( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert return_code + out = _read_stdout(capsys) + assert "No wallet provided." in out + + # Clear the captured content + capsys.readouterr() + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--sender", + "invalidAlias", + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert return_code + out = _read_stdout(capsys) + 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 / "wallets.mxpy.json" + import multiversx_sdk_cli.config_wallet + + json_file = { + "active": "alice", + "wallets": { + "alice": { + "index": "0", + }, + }, + } + test_file.write_text(json.dumps(json_file)) + + 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( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert return_code + out = _read_stdout(capsys) + assert "'path' field must be set in the wallet 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 "'path' field must be set in the wallet config." in out + + # Clear the captured content + capsys.readouterr() + + json_file = { + "active": "alice", + "wallets": { + "alice": { + "kind": "keystore", + "path": "/example/to/wallet.json", + "index": "0", + }, + }, + } + test_file.write_text(json.dumps(json_file)) + + 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: "") + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert return_code + + return_code = main( + [ + "tx", + "new", + "--receiver", + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--gas-limit", + "50000", + "--sender", + "alice", + "--nonce", + "0", + "--chain", + "D", + ] + ) + assert return_code + + +def _read_stdout(capsys: Any) -> str: + stdout: str = capsys.readouterr().out.strip() + return stdout 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..65887c12 --- /dev/null +++ b/multiversx_sdk_cli/tests/test_cli_governance.py @@ -0,0 +1,213 @@ +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) + 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 new file mode 100644 index 00000000..8af13e59 --- /dev/null +++ b/multiversx_sdk_cli/tests/test_cli_multisig.py @@ -0,0 +1,959 @@ +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" +adder_abi = testdata / "adder.abi.json" + + +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 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@64697374726962757465" + ) + + +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 + + +def get_transaction(capsys: Any) -> dict[str, Any]: + out = _read_stdout(capsys) + output: dict[str, Any] = json.loads(out) + tx: dict[str, Any] = output["emittedTransaction"] + return tx 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 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 "" 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 diff --git a/pyproject.toml b/pyproject.toml index a7b38afc..69697e62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,14 +4,14 @@ build-backend = "hatchling.build" [project] name = "multiversx-sdk-cli" -version = "10.2.0" +version = "11.0.0" authors = [ { name="MultiversX" }, ] 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", @@ -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.2" ] [project.scripts] diff --git a/requirements.txt b/requirements.txt index 7f295a29..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.2.0 +multiversx-sdk[ledger]==1.6.2