diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index e7010de2..b157b6e8 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -47,13 +47,4 @@ jobs: shell: bash run: | export PYTHONPATH=. - pytest -m "not skip_on_windows" . - - name: Run CLI tests - shell: bash - run: | - export PROXY=https://testnet-gateway.multiversx.com - export CHAIN_ID=T - cd ./multiversx_sdk_cli/tests - source ./test_cli_tx.sh && testAll || return 1 - source ./test_cli_dns.sh && testOffline || return 1 - source ./test_cli_validators.sh && testAll || return 1 + pytest -m "not skip_on_windows and not require_localnet" . diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9dda9a37..044f4b88 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,8 +44,3 @@ jobs: run: | export PYTHONPATH=. pytest . - - name: Run CLI tests - run: | - cd ./multiversx_sdk_cli/tests - source ./test_cli_contracts.sh && testAll || return 1 - source ./test_cli_dns.sh && testOffline || return 1 diff --git a/.github/workflows/test-localnet-tests.yml b/.github/workflows/test-localnet-tests.yml new file mode 100644 index 00000000..8c62ad80 --- /dev/null +++ b/.github/workflows/test-localnet-tests.yml @@ -0,0 +1,55 @@ +name: Test localnet-dependent tests + +on: + pull_request: + branches: [main, feat/*] + workflow_dispatch: + +env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + +jobs: + localnet: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + python-version: [3.11] + + steps: + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python3 -m pip install --upgrade pip + pip3 install -r requirements.txt + pip3 install -r ./requirements-dev.txt --upgrade + + - name: Set up MultiversX localnet + run: | + mkdir -p ~/multiversx-sdk + 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 + sleep 120 + + - name: Test localnet dependent tests + run: | + pytest -m require_localnet . + python3 -m multiversx_sdk_cli.cli localnet clean --configfile=./multiversx_sdk_cli/tests/testdata/localnet_with_resolution_remote.toml diff --git a/CLI.md b/CLI.md index 3cf655de..fc9f0c77 100644 --- a/CLI.md +++ b/CLI.md @@ -218,15 +218,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 @@ -311,15 +315,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --function FUNCTION the function to call --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, @@ -414,15 +422,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --arguments ARGUMENTS [ARGUMENTS ...] arguments for the contract transaction, as [number, bech32-address, ascii string, boolean] or hex-encoded. E.g. --arguments 42 0x64 1000 @@ -520,10 +532,10 @@ usage: mxpy tx COMMAND [-h] ... Create and broadcast Transactions COMMANDS: - {new,send,get,sign} + {new,send,get,sign,relay} OPTIONS: - -h, --help show this help message and exit + -h, --help show this help message and exit ---------------- COMMANDS summary @@ -532,6 +544,7 @@ new Create a new transaction. send Send a previously saved transaction. get Get a transaction. sign Sign a previously saved transaction. +relay Relay a previously saved transaction. ``` ### Transactions.New @@ -577,15 +590,19 @@ options: --data DATA the payload, or 'memo' of the transaction (default: ) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --data-file DATA_FILE a file containing transaction data --token-transfers TOKEN_TRANSFERS [TOKEN_TRANSFERS ...] @@ -730,15 +747,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -786,15 +807,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -840,15 +865,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -894,15 +923,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -948,15 +981,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1002,15 +1039,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1094,15 +1135,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1167,15 +1212,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1223,15 +1272,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1279,15 +1332,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1335,15 +1392,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1391,15 +1452,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1447,15 +1512,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1502,15 +1571,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1557,15 +1630,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1613,15 +1690,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1669,15 +1750,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) @@ -1726,15 +1811,19 @@ options: --value VALUE the value to transfer (default: 0) --chain CHAIN the chain identifier --version VERSION the transaction version (default: 2) + --relayer RELAYER the bech32 address of the relayer + --relayer-pem RELAYER_PEM 🔑 the PEM file, if keyfile not provided + --relayer-pem-index RELAYER_PEM_INDEX 🔑 the index in the PEM file (default: 0) + --relayer-keyfile RELAYER_KEYFILE 🔑 a JSON keyfile, if PEM not provided + --relayer-passfile RELAYER_PASSFILE 🔑 a file containing keyfile's password, if keyfile provided + --relayer-ledger 🔐 bool flag for signing transaction using ledger + --relayer-ledger-account-index RELAYER_LEDGER_ACCOUNT_INDEX + 🔐 the index of the account when using Ledger + --relayer-ledger-address-index RELAYER_LEDGER_ADDRESS_INDEX + 🔐 the index of the address when using Ledger --guardian GUARDIAN the address of the guradian --guardian-service-url GUARDIAN_SERVICE_URL the url of the guardian service --guardian-2fa-code GUARDIAN_2FA_CODE the 2fa code for the guardian - --relayer RELAYER the address of the relayer - --inner-transactions INNER_TRANSACTIONS a json file containing the inner transactions; should only be provided - when creating the relayer's transaction - --inner-transactions-outfile INNER_TRANSACTIONS_OUTFILE - where to save the transaction as an inner transaction (default: - stdout) --options OPTIONS the transaction options (default: 0) --send ✓ whether to broadcast the transaction (default: False) --simulate whether to simulate the transaction (default: False) diff --git a/README.md b/README.md index 881b9041..89d55637 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Python Command Line Tools for interacting with MultiversX. [CLI](CLI.md) ## Distribution -[mxpy-up](https://docs.multiversx.com/sdk-and-tools/sdk-py/installing-mxpy/) and [PyPi](https://pypi.org/project/multiversx-sdk-cli/#history) +[pipx](https://docs.multiversx.com/sdk-and-tools/sdk-py/installing-mxpy/) [(PyPi)](https://pypi.org/project/multiversx-sdk-cli/#history) ## Development setup diff --git a/multiversx_sdk_cli/accounts.py b/multiversx_sdk_cli/accounts.py index 489f03a7..38060b92 100644 --- a/multiversx_sdk_cli/accounts.py +++ b/multiversx_sdk_cli/accounts.py @@ -6,7 +6,7 @@ TransactionComputer, UserSigner) from multiversx_sdk.network_providers.accounts import AccountOnNetwork -from multiversx_sdk_cli.constants import DEFAULT_HRP +from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.interfaces import IAccount, IAddress, ITransaction from multiversx_sdk_cli.ledger.config import compare_versions from multiversx_sdk_cli.ledger.ledger_app_handler import \ @@ -61,11 +61,11 @@ def __init__(self, if pem_file: pem_path = Path(pem_file).expanduser().resolve() self.signer = UserSigner.from_pem_file(pem_path, pem_index) - self.address = Address(self.signer.get_pubkey().buffer, DEFAULT_HRP) + self.address = Address(self.signer.get_pubkey().buffer, get_address_hrp()) elif key_file and password: key_file_path = Path(key_file).expanduser().resolve() self.signer = UserSigner.from_wallet(key_file_path, password) - self.address = Address(self.signer.get_pubkey().buffer, DEFAULT_HRP) + self.address = Address(self.signer.get_pubkey().buffer, get_address_hrp()) def sign_transaction(self, transaction: ITransaction) -> str: assert self.signer is not None @@ -96,6 +96,8 @@ def __init__(self, account_index: int = 0, address_index: int = 0) -> None: def sign_transaction(self, transaction: ITransaction) -> str: ledger_version = do_get_ledger_version() should_use_hash_signing = compare_versions(ledger_version, SIGN_USING_HASH_VERSION) >= 0 + + # TODO: This check will be removed in the next major release. if should_use_hash_signing: transaction.version = TX_HASH_SIGN_VERSION transaction.options = transaction.options | TX_HASH_SIGN_OPTIONS diff --git a/multiversx_sdk_cli/cli.py b/multiversx_sdk_cli/cli.py index 79eacf20..76fe95e1 100644 --- a/multiversx_sdk_cli/cli.py +++ b/multiversx_sdk_cli/cli.py @@ -6,6 +6,7 @@ from typing import Any, List import argcomplete +from multiversx_sdk import LibraryConfig from rich.logging import RichHandler import multiversx_sdk_cli.cli_accounts @@ -53,6 +54,8 @@ def _do_main(cli_args: List[str]): logging.basicConfig(level="INFO", format='%(name)s: %(message)s', handlers=[RichHandler(show_time=False, rich_tracebacks=True)]) verify_deprecated_entries_in_config_file() + default_hrp = config.get_address_hrp() + LibraryConfig.default_address_hrp = default_hrp if not hasattr(args, "func"): parser.print_help() diff --git a/multiversx_sdk_cli/cli_accounts.py b/multiversx_sdk_cli/cli_accounts.py index 9ae5933a..f7a5afe5 100644 --- a/multiversx_sdk_cli/cli_accounts.py +++ b/multiversx_sdk_cli/cli_accounts.py @@ -4,6 +4,7 @@ from multiversx_sdk import Address, ProxyNetworkProvider from multiversx_sdk_cli import cli_shared, utils +from multiversx_sdk_cli.config import get_config_for_network_providers logger = logging.getLogger("cli.accounts") @@ -33,7 +34,8 @@ def _add_address_arg(sub: Any): def get_account(args: Any): proxy_url = args.proxy address = args.address - proxy = ProxyNetworkProvider(proxy_url) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=proxy_url, config=config) account = proxy.get_account(Address.new_from_bech32(address)) if args.balance: diff --git a/multiversx_sdk_cli/cli_contracts.py b/multiversx_sdk_cli/cli_contracts.py index 8f7c14a9..9516bcf4 100644 --- a/multiversx_sdk_cli/cli_contracts.py +++ b/multiversx_sdk_cli/cli_contracts.py @@ -10,6 +10,7 @@ from multiversx_sdk_cli import cli_shared, projects, utils from multiversx_sdk_cli.cli_output import CLIOutputBuilder +from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS from multiversx_sdk_cli.contract_verification import \ trigger_contract_verification @@ -100,7 +101,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False) _add_function_arg(sub) _add_arguments_arg(sub) - _add_token_transfers_args(sub) + 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" @@ -243,12 +244,6 @@ def _add_arguments_arg(sub: Any): "E.g. [{ 'to': 'erd1...', 'amount': 10000000000 }]") -def _add_token_transfers_args(sub: Any): - sub.add_argument("--token-transfers", nargs='+', - help="token transfers for transfer & execute, as [token, amount] " - "E.g. --token-transfers NFT-123456-0a 1 ESDT-987654 100000000") - - def _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)") @@ -448,14 +443,15 @@ def query(args: Any): args.chain = "" cli_shared.prepare_chain_id_in_args(args) - config = TransactionsFactoryConfig(args.chain) + factory_config = TransactionsFactoryConfig(args.chain) abi = Abi.load(Path(args.abi)) if args.abi else None - contract = SmartContract(config, abi) + contract = SmartContract(factory_config, abi) arguments, should_prepare_args = _get_contract_arguments(args) contract_address = Address.new_from_bech32(args.contract) - proxy = ProxyNetworkProvider(args.proxy) + network_provider_config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=network_provider_config) function = args.function result = contract.query_contract( diff --git a/multiversx_sdk_cli/cli_delegation.py b/multiversx_sdk_cli/cli_delegation.py index aafbaa4b..cc6dcaa2 100644 --- a/multiversx_sdk_cli/cli_delegation.py +++ b/multiversx_sdk_cli/cli_delegation.py @@ -3,6 +3,7 @@ from multiversx_sdk import ProxyNetworkProvider, TransactionsFactoryConfig from multiversx_sdk_cli import cli_shared, errors, utils +from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.delegation import DelegationOperations @@ -209,7 +210,8 @@ def do_create_delegation_contract(args: Any): def get_contract_address_by_deploy_tx_hash(args: Any): args = utils.as_object(args) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) transaction = proxy.get_transaction(args.create_tx_hash) transaction_events = transaction.logs.events diff --git a/multiversx_sdk_cli/cli_dns.py b/multiversx_sdk_cli/cli_dns.py index 80e93747..dbbc9c9b 100644 --- a/multiversx_sdk_cli/cli_dns.py +++ b/multiversx_sdk_cli/cli_dns.py @@ -1,10 +1,11 @@ from typing import Any, List -from multiversx_sdk import Address, ProxyNetworkProvider +from multiversx_sdk import ProxyNetworkProvider from prettytable import PrettyTable from multiversx_sdk_cli import cli_shared -from multiversx_sdk_cli.constants import ADDRESS_ZERO_BECH32 +from multiversx_sdk_cli.config import get_config_for_network_providers +from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX from multiversx_sdk_cli.dns import (compute_dns_address_for_shard_id, dns_address_for_name, name_hash, register, registration_cost, resolve, validate_name, @@ -79,14 +80,17 @@ def _ensure_proxy_is_provided(args: Any): def dns_resolve(args: Any): _ensure_proxy_is_provided(args) - addr = resolve(args.name, ProxyNetworkProvider(args.proxy)) - if addr.to_hex() != Address.new_from_bech32(ADDRESS_ZERO_BECH32).to_hex(): + config = get_config_for_network_providers() + addr = resolve(args.name, ProxyNetworkProvider(url=args.proxy, config=config)) + if addr.to_hex() != ADDRESS_ZERO_HEX: print(addr.to_bech32()) def dns_validate_name(args: Any): _ensure_proxy_is_provided(args) - validate_name(args.name, args.shard_id, ProxyNetworkProvider(args.proxy)) + + config = get_config_for_network_providers() + validate_name(args.name, args.shard_id, ProxyNetworkProvider(url=args.proxy, config=config)) def get_name_hash(args: Any): @@ -107,13 +111,16 @@ def get_dns_address_for_name_hex(args: Any): def get_registration_cost(args: Any): _ensure_proxy_is_provided(args) - print(registration_cost(args.shard_id, ProxyNetworkProvider(args.proxy))) + + config = get_config_for_network_providers() + print(registration_cost(args.shard_id, ProxyNetworkProvider(url=args.proxy, config=config))) def get_version(args: Any): _ensure_proxy_is_provided(args) - proxy = ProxyNetworkProvider(args.proxy) + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) if args.all: t = PrettyTable(['Shard ID', 'Contract address (bech32)', 'Contract address (hex)', 'Version']) for shard_id in range(0, 256): diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 867c2e1f..5ce01dde 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -14,7 +14,6 @@ load_password) from multiversx_sdk_cli.constants import (DEFAULT_TX_VERSION, TRANSACTION_OPTIONS_TX_GUARDED) -from multiversx_sdk_cli.custom_network_provider import CustomNetworkProvider from multiversx_sdk_cli.errors import ArgumentsNotProvidedError from multiversx_sdk_cli.interfaces import ITransaction from multiversx_sdk_cli.ledger.ledger_functions import do_get_ledger_address @@ -69,7 +68,8 @@ def add_tx_args( with_nonce: bool = True, with_receiver: bool = True, with_data: bool = True, - with_estimate_gas: bool = False): + with_estimate_gas: bool = False, + with_relayer_wallet_args: bool = True): if with_nonce: sub.add_argument("--nonce", type=int, required=not ("--recall-nonce" in args), help="# the nonce for the transaction") sub.add_argument("--recall-nonce", action="store_true", default=False, help="⭮ whether to recall the nonce when creating the transaction (default: %(default)s)") @@ -91,18 +91,15 @@ def add_tx_args( sub.add_argument("--chain", help="the chain identifier") sub.add_argument("--version", type=int, default=DEFAULT_TX_VERSION, help="the transaction version (default: %(default)s)") + sub.add_argument("--relayer", help="the bech32 address of the relayer") + if with_relayer_wallet_args: + add_relayed_v3_wallet_args(args, sub) + add_guardian_args(sub) - add_relayed_v3_args(sub) sub.add_argument("--options", type=int, default=0, help="the transaction options (default: 0)") -def add_relayed_v3_args(sub: Any): - sub.add_argument("--relayer", help="the address of the relayer") - sub.add_argument("--inner-transactions", help="a json file containing the inner transactions; should only be provided when creating the relayer's transaction") - sub.add_argument("--inner-transactions-outfile", type=str, help="where to save the transaction as an inner transaction (default: stdout)") - - def add_guardian_args(sub: Any): sub.add_argument("--guardian", type=str, help="the address of the guradian", default="") sub.add_argument("--guardian-service-url", type=str, help="the url of the guardian service", default="") @@ -130,6 +127,17 @@ def add_guardian_wallet_args(args: List[str], sub: Any): sub.add_argument("--guardian-ledger-address-index", type=int, default=0, help="🔐 the index of the address when using Ledger") +# Required check not properly working, same for guardian. Will be refactored in the future. +def add_relayed_v3_wallet_args(args: List[str], sub: Any): + sub.add_argument("--relayer-pem", help="🔑 the PEM file, if keyfile not provided") + sub.add_argument("--relayer-pem-index", type=int, default=0, help="🔑 the index in the PEM file (default: %(default)s)") + sub.add_argument("--relayer-keyfile", help="🔑 a JSON keyfile, if PEM not provided") + sub.add_argument("--relayer-passfile", help="🔑 a file containing keyfile's password, if keyfile provided") + sub.add_argument("--relayer-ledger", action="store_true", default=False, help="🔐 bool flag for signing transaction using ledger") + sub.add_argument("--relayer-ledger-account-index", type=int, default=0, help="🔐 the index of the account when using Ledger") + sub.add_argument("--relayer-ledger-address-index", type=int, default=0, help="🔐 the index of the address when using Ledger") + + def add_proxy_arg(sub: Any): sub.add_argument("--proxy", help="🔗 the URL of the proxy") @@ -148,6 +156,12 @@ def add_omit_fields_arg(sub: Any): sub.add_argument("--omit-fields", default="[]", type=str, required=False, help="omit fields in the output payload (default: %(default)s)") +def add_token_transfers_args(sub: Any): + sub.add_argument("--token-transfers", nargs='+', + help="token transfers for transfer & execute, as [token, amount] " + "E.g. --token-transfers NFT-123456-0a 1 ESDT-987654 100000000") + + def parse_omit_fields_arg(args: Any) -> List[str]: literal = args.omit_fields parsed = ast.literal_eval(literal) @@ -168,6 +182,20 @@ def prepare_account(args: Any): return account +def prepare_relayer_account(args: Any) -> Account: + if args.relayer_ledger: + account = LedgerAccount(account_index=args.relayer_ledger_account_index, address_index=args.relayer_ledger_address_index) + if args.relayer_pem: + account = Account(pem_file=args.relayer_pem, pem_index=args.relayer_pem_index) + elif args.relayer_keyfile: + password = load_password(args) + account = Account(key_file=args.relayer_keyfile, password=password) + else: + raise errors.NoWalletProvided() + + return account + + def prepare_guardian_account(args: Any): if args.guardian_pem: account = Account(pem_file=args.guardian_pem, pem_index=args.guardian_pem_index) @@ -189,7 +217,8 @@ def prepare_nonce_in_args(args: Any): if args.recall_nonce: account = prepare_account(args) - account.sync_nonce(ProxyNetworkProvider(args.proxy)) + network_provider_config = config.get_config_for_network_providers() + account.sync_nonce(ProxyNetworkProvider(url=args.proxy, config=network_provider_config)) args.nonce = account.nonce @@ -198,7 +227,8 @@ def prepare_chain_id_in_args(args: Any): raise ArgumentsNotProvidedError("chain ID cannot be decided: `--chain` or `--proxy` should be provided") if args.chain and args.proxy: - proxy = ProxyNetworkProvider(args.proxy) + network_provider_config = config.get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=network_provider_config) fetched_chain_id = proxy.get_network_config().chain_id if args.chain != fetched_chain_id: @@ -211,7 +241,8 @@ def prepare_chain_id_in_args(args: Any): if args.chain: return elif args.proxy: - proxy = ProxyNetworkProvider(args.proxy) + network_provider_config = config.get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=network_provider_config) args.chain = proxy.get_network_config().chain_id @@ -260,7 +291,8 @@ def check_options_for_guarded_tx(options: int): def send_or_simulate(tx: ITransaction, args: Any, dump_output: bool = True) -> CLIOutputBuilder: - proxy = CustomNetworkProvider(args.proxy) + network_provider_config = config.get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=network_provider_config) is_set_wait_result = hasattr(args, "wait_result") and args.wait_result is_set_send = hasattr(args, "send") and args.send diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 924e4c9d..06c7a0b9 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -1,17 +1,16 @@ import logging from pathlib import Path -from typing import Any, Dict, List +from typing import Any, List -from multiversx_sdk import Transaction, TransactionsConverter +from multiversx_sdk import ProxyNetworkProvider from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.cli_output import CLIOutputBuilder +from multiversx_sdk_cli.config import get_config_for_network_providers from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.custom_network_provider import CustomNetworkProvider -from multiversx_sdk_cli.errors import BadUsage, NoWalletProvided +from multiversx_sdk_cli.errors import IncorrectWalletError, NoWalletProvided from multiversx_sdk_cli.transactions import (compute_relayed_v1_data, do_prepare_transaction, - load_inner_transactions_from_file, load_transaction_from_file) logger = logging.getLogger("cli.transactions") @@ -23,7 +22,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: sub = cli_shared.add_command_subparser(subparsers, "tx", "new", f"Create a new transaction.{CLIOutputBuilder.describe()}") _add_common_arguments(args, sub) - _add_token_transfers_args(sub) + cli_shared.add_token_transfers_args(sub) cli_shared.add_outfile_arg(sub, what="signed transaction, hash") cli_shared.add_broadcast_args(sub, relay=True) cli_shared.add_proxy_arg(sub) @@ -58,6 +57,14 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: cli_shared.add_guardian_wallet_args(args, sub) sub.set_defaults(func=sign_transaction) + sub = cli_shared.add_command_subparser(subparsers, "tx", "relay", f"Relay a previously saved transaction.{CLIOutputBuilder.describe()}") + cli_shared.add_relayed_v3_wallet_args(args, sub) + cli_shared.add_infile_arg(sub, what="a previously saved transaction") + cli_shared.add_outfile_arg(sub, what="the relayer signed transaction") + cli_shared.add_broadcast_args(sub) + cli_shared.add_proxy_arg(sub) + sub.set_defaults(func=relay_transaction) + parser.epilog = cli_shared.build_group_epilog(subparsers) return subparsers @@ -68,12 +75,6 @@ def _add_common_arguments(args: List[str], sub: Any): sub.add_argument("--data-file", type=str, default=None, help="a file containing transaction data") -def _add_token_transfers_args(sub: Any): - sub.add_argument("--token-transfers", nargs='+', - help="token transfers for transfer & execute, as [token, amount] " - "E.g. --token-transfers NFT-123456-0a 1 ESDT-987654 100000000") - - def create_transaction(args: Any): args = utils.as_object(args) @@ -85,14 +86,8 @@ def create_transaction(args: Any): if args.data_file: args.data = Path(args.data_file).read_text() - check_relayer_transaction_with_data_field_for_relayed_v3(args) - tx = do_prepare_transaction(args) - if hasattr(args, "inner_transactions_outfile") and args.inner_transactions_outfile: - save_transaction_to_inner_transactions_file(tx, args) - return - if hasattr(args, "relay") and args.relay: logger.warning("RelayedV1 transactions are deprecated. Please use RelayedV3 instead.") args.outfile.write(compute_relayed_v1_data(tx)) @@ -101,36 +96,14 @@ def create_transaction(args: Any): cli_shared.send_or_simulate(tx, args) -def save_transaction_to_inner_transactions_file(transaction: Transaction, args: Any): - inner_txs_file = Path(args.inner_transactions_outfile).expanduser() - transactions = get_inner_transactions_if_any(inner_txs_file) - transactions.append(transaction) - - tx_converter = TransactionsConverter() - inner_transactions: Dict[str, Any] = {} - inner_transactions["innerTransactions"] = [tx_converter.transaction_to_dictionary(tx) for tx in transactions] - - with open(inner_txs_file, "w") as file: - utils.dump_out_json(inner_transactions, file) - - -def get_inner_transactions_if_any(file: Path) -> List[Transaction]: - if file.is_file(): - return load_inner_transactions_from_file(file) - return [] - - -def check_relayer_transaction_with_data_field_for_relayed_v3(args: Any): - if hasattr(args, "inner_transactions") and args.inner_transactions and args.data: - raise BadUsage("Can't set data field when creating a relayedV3 transaction") - - def send_transaction(args: Any): args = utils.as_object(args) tx = load_transaction_from_file(args.infile) output = CLIOutputBuilder() - proxy = CustomNetworkProvider(args.proxy) + + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) try: tx_hash = proxy.send_transaction(tx) @@ -143,7 +116,9 @@ def send_transaction(args: Any): def get_transaction(args: Any): args = utils.as_object(args) omit_fields = cli_shared.parse_omit_fields_arg(args) - proxy = CustomNetworkProvider(args.proxy) + + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider(url=args.proxy, config=config) transaction = proxy.get_transaction(args.hash, True) output = CLIOutputBuilder().set_transaction_on_network(transaction, omit_fields).build() @@ -174,3 +149,26 @@ def sign_transaction(args: Any): tx = cosign_transaction(tx, args.guardian_service_url, args.guardian_2fa_code) cli_shared.send_or_simulate(tx, args) + + +def relay_transaction(args: Any): + args = utils.as_object(args) + + if not _is_relayer_wallet_provided(args): + raise NoWalletProvided() + + cli_shared.check_broadcast_args(args) + + tx = load_transaction_from_file(args.infile) + relayer = cli_shared.prepare_relayer_account(args) + + if tx.relayer != relayer.address.to_bech32(): + raise IncorrectWalletError("Relayer wallet does not match the relayer's address set in the transaction.") + + tx.relayer_signature = bytes.fromhex(relayer.sign_transaction(tx)) + + cli_shared.send_or_simulate(tx, args) + + +def _is_relayer_wallet_provided(args: Any): + return any([args.relayer_pem, args.relayer_keyfile, args.relayer_ledger]) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index eeae9ebb..1f8b2082 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -10,7 +10,8 @@ from multiversx_sdk.core.address import get_shard_of_pubkey from multiversx_sdk_cli import cli_shared, utils -from multiversx_sdk_cli.constants import DEFAULT_HRP, NUMBER_OF_SHARDS +from multiversx_sdk_cli.config import get_address_hrp +from multiversx_sdk_cli.constants import NUMBER_OF_SHARDS from multiversx_sdk_cli.errors import (BadUserInput, KnownError, WalletGenerationError) from multiversx_sdk_cli.sign_verify import SignedMessage, sign_message @@ -54,7 +55,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: ) sub.add_argument("--format", choices=WALLET_FORMATS, help="the format of the generated wallet file (default: %(default)s)", default=None) sub.add_argument("--outfile", help="the output path and base file name for the generated wallet files (default: %(default)s)", type=str) - sub.add_argument("--address-hrp", help=f"the human-readable part of the address, when format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM} (default: %(default)s)", type=str, default=DEFAULT_HRP) + sub.add_argument("--address-hrp", help=f"the human-readable part of the address, when format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM} (default: %(default)s)", type=str, default=get_address_hrp()) sub.add_argument("--shard", type=int, help="the shard in which the address will be generated; (default: random)") sub.set_defaults(func=wallet_new) @@ -69,7 +70,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: sub.add_argument("--in-format", required=True, choices=WALLET_FORMATS, help="the format of the input file") sub.add_argument("--out-format", required=True, choices=WALLET_FORMATS_AND_ADDRESSES, help="the format of the output file") sub.add_argument("--address-index", help=f"the address index, if input format is {WALLET_FORMAT_RAW_MNEMONIC}, {WALLET_FORMAT_KEYSTORE_MNEMONIC} or {WALLET_FORMAT_PEM} (with multiple entries) and the output format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM}", type=int, default=0) - sub.add_argument("--address-hrp", help=f"the human-readable part of the address, when the output format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM} (default: %(default)s)", type=str, default=DEFAULT_HRP) + sub.add_argument("--address-hrp", help=f"the human-readable part of the address, when the output format is {WALLET_FORMAT_KEYSTORE_SECRET_KEY} or {WALLET_FORMAT_PEM} (default: %(default)s)", type=str, default=get_address_hrp()) sub.set_defaults(func=convert_wallet) sub = cli_shared.add_command_subparser( @@ -82,6 +83,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any: group = sub.add_mutually_exclusive_group(required=True) group.add_argument("--encode", action="store_true", help="whether to encode") group.add_argument("--decode", action="store_true", help="whether to decode") + sub.add_argument("--hrp", type=str, help="the human readable part; only used for encoding to bech32 (default: %(default)s)", default=get_address_hrp()) sub.set_defaults(func=do_bech32) sub = cli_shared.add_command_subparser( @@ -288,7 +290,8 @@ def do_bech32(args: Any): value = args.value if encode: - address = Address.new_from_hex(value, DEFAULT_HRP) + hrp = args.hrp + address = Address.new_from_hex(value, hrp) result = address.to_bech32() else: address = Address.new_from_bech32(value) diff --git a/multiversx_sdk_cli/config.py b/multiversx_sdk_cli/config.py index 3b54049f..621ab5b1 100644 --- a/multiversx_sdk_cli/config.py +++ b/multiversx_sdk_cli/config.py @@ -2,6 +2,8 @@ from pathlib import Path from typing import Any, Dict, List +from multiversx_sdk import NetworkProviderConfig + from multiversx_sdk_cli import errors, utils SDK_PATH = Path("~/multiversx-sdk").expanduser().resolve() @@ -58,6 +60,10 @@ def get_value(name: str) -> str: return value +def get_address_hrp(): + return get_value("default_address_hrp") + + def set_value(name: str, value: Any): _guard_valid_name(name) data = read_file() @@ -160,6 +166,7 @@ def get_defaults() -> Dict[str, Any]: "dependencies.testwallets.urlTemplate.windows": "https://github.com/multiversx/mx-sdk-testwallets/archive/{TAG}.tar.gz", "dependencies.wasm-opt.tag": "0.112.0", "github_api_token": "", + "default_address_hrp": "erd" } @@ -242,3 +249,7 @@ def get_dependency_directory(key: str, tag: str) -> Path: def get_dependency_parent_directory(key: str) -> Path: return SDK_PATH / key + + +def get_config_for_network_providers() -> NetworkProviderConfig: + return NetworkProviderConfig(client_name="mxpy") diff --git a/multiversx_sdk_cli/constants.py b/multiversx_sdk_cli/constants.py index fabd8809..0f0e0dbc 100644 --- a/multiversx_sdk_cli/constants.py +++ b/multiversx_sdk_cli/constants.py @@ -9,7 +9,6 @@ DEFAULT_TX_VERSION = 2 -DEFAULT_HRP = "erd" -ADDRESS_ZERO_BECH32 = "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu" +ADDRESS_ZERO_HEX = "0000000000000000000000000000000000000000000000000000000000000000" NUMBER_OF_SHARDS = 3 diff --git a/multiversx_sdk_cli/contracts.py b/multiversx_sdk_cli/contracts.py index 98885dc4..5f8214b9 100644 --- a/multiversx_sdk_cli/contracts.py +++ b/multiversx_sdk_cli/contracts.py @@ -11,7 +11,7 @@ from multiversx_sdk_cli import errors from multiversx_sdk_cli.accounts import Account -from multiversx_sdk_cli.constants import DEFAULT_HRP +from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.interfaces import IAddress logger = logging.getLogger("contracts") @@ -211,7 +211,7 @@ def _prepare_args_for_factory(self, arguments: List[str]) -> List[Any]: args.append(self._hex_to_bytes(arg)) elif arg.isnumeric(): args.append(int(arg)) - elif arg.startswith(DEFAULT_HRP): + elif arg.startswith(get_address_hrp()): args.append(Address.new_from_bech32(arg)) elif arg.lower() == FALSE_STR_LOWER: args.append(False) @@ -253,7 +253,7 @@ def _to_hex(arg: str): if arg.isnumeric(): return _prepare_decimal(arg) - elif arg.startswith(DEFAULT_HRP): + elif arg.startswith(get_address_hrp()): addr = Address.from_bech32(arg) return _prepare_hexadecimal(f"{HEX_PREFIX}{addr.hex()}") elif arg.lower() == FALSE_STR_LOWER or arg.lower() == TRUE_STR_LOWER: diff --git a/multiversx_sdk_cli/cosign_transaction.py b/multiversx_sdk_cli/cosign_transaction.py index fce9e274..d63d71f0 100644 --- a/multiversx_sdk_cli/cosign_transaction.py +++ b/multiversx_sdk_cli/cosign_transaction.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any import requests from multiversx_sdk import TransactionsConverter @@ -11,20 +11,22 @@ def cosign_transaction(transaction: ITransaction, service_url: str, guardian_cod tx_converter = TransactionsConverter() payload = { "code": f"{guardian_code}", - "transaction": tx_converter.transaction_to_dictionary(transaction) + "transactions": [tx_converter.transaction_to_dictionary(transaction)] } - url = f"{service_url}/sign-transaction" + # we call sign-multiple-transactions to be allowed a bigger payload (e.g. deploying large contracts) + url = f"{service_url}/sign-multiple-transactions" response = requests.post(url, json=payload) check_for_guardian_error(response.json()) - tx_as_dict = response.json()["data"]["transaction"] + # we only send 1 transaction + tx_as_dict = response.json()["data"]["transactions"][0] transaction.guardian_signature = bytes.fromhex(tx_as_dict["guardianSignature"]) return transaction -def check_for_guardian_error(response: Dict[str, Any]): +def check_for_guardian_error(response: dict[str, Any]): error = response["error"] if error: diff --git a/multiversx_sdk_cli/custom_network_provider.py b/multiversx_sdk_cli/custom_network_provider.py deleted file mode 100644 index e11c9870..00000000 --- a/multiversx_sdk_cli/custom_network_provider.py +++ /dev/null @@ -1,36 +0,0 @@ -from typing import Any, Dict, Optional, Protocol - -from multiversx_sdk import GenericError, ProxyNetworkProvider - -from multiversx_sdk_cli.errors import ProxyError -from multiversx_sdk_cli.interfaces import ISimulateResponse, ITransaction - - -class ITransactionOnNetwork(Protocol): - hash: str - is_completed: Optional[bool] - - def to_dictionary(self) -> Dict[str, Any]: - ... - - -class CustomNetworkProvider: - def __init__(self, url: str) -> None: - self._provider = ProxyNetworkProvider(url) - - def send_transaction(self, transaction: ITransaction) -> str: - try: - hash = self._provider.send_transaction(transaction) - return hash - except GenericError as ge: - url = ge.url - message = ge.data.get("error", "") - data = ge.data.get("data", "") - code = ge.data.get("code", "") - raise ProxyError(message, url, data, code) - - def get_transaction(self, tx_hash: str, with_process_status: Optional[bool] = False) -> ITransactionOnNetwork: - return self._provider.get_transaction(tx_hash, with_process_status) - - def simulate_transaction(self, transaction: ITransaction) -> ISimulateResponse: - return self._provider.simulate_transaction(transaction) diff --git a/multiversx_sdk_cli/delegation/staking_provider.py b/multiversx_sdk_cli/delegation/staking_provider.py index 418418dc..977e7424 100644 --- a/multiversx_sdk_cli/delegation/staking_provider.py +++ b/multiversx_sdk_cli/delegation/staking_provider.py @@ -5,6 +5,7 @@ Transaction, ValidatorPublicKey) from multiversx_sdk.core.serializer import args_to_string +from multiversx_sdk_cli.config import get_address_hrp from multiversx_sdk_cli.errors import BadUsage from multiversx_sdk_cli.interfaces import IAddress, ITransaction from multiversx_sdk_cli.validators.validators_file import ValidatorsFile @@ -408,7 +409,7 @@ def prepare_transaction_for_setting_metadata(self, owner: IAccount, args: Any) - return tx def prepare_transaction_for_creating_delegation_contract_from_validator(self, owner: IAccount, args: Any) -> ITransaction: - receiver = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6" + receiver = Address.new_from_hex("000000000000000000010000000000000000000000000000000000000004ffff", get_address_hrp()).to_bech32() max_cap = int(args.max_cap) fee = int(args.fee) data = "makeNewContractFromValidatorData@" + args_to_string([max_cap, fee]) diff --git a/multiversx_sdk_cli/dns.py b/multiversx_sdk_cli/dns.py index 114b1a11..61e387c4 100644 --- a/multiversx_sdk_cli/dns.py +++ b/multiversx_sdk_cli/dns.py @@ -6,7 +6,8 @@ from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.accounts import Account -from multiversx_sdk_cli.constants import ADDRESS_ZERO_BECH32, DEFAULT_HRP +from multiversx_sdk_cli.config import get_address_hrp +from multiversx_sdk_cli.constants import ADDRESS_ZERO_HEX from multiversx_sdk_cli.contracts import SmartContract from multiversx_sdk_cli.transactions import (compute_relayed_v1_data, do_prepare_transaction) @@ -36,10 +37,10 @@ def resolve(name: str, proxy: INetworkProvider) -> Address: ) if len(response) == 0: - return Address.from_bech32(ADDRESS_ZERO_BECH32) + return Address.new_from_hex(ADDRESS_ZERO_HEX, get_address_hrp()) result = response[0].get("returnDataParts")[0] - return Address.from_hex(result, DEFAULT_HRP) + return Address.new_from_hex(result, get_address_hrp()) def validate_name(name: str, shard_id: int, proxy: INetworkProvider): @@ -137,7 +138,7 @@ def compute_dns_address_for_shard_id(shard_id: int) -> Address: deployer_pubkey_prefix = InitialDNSAddress[:len(InitialDNSAddress) - ShardIdentiferLen] deployer_pubkey = deployer_pubkey_prefix + bytes([0, shard_id]) - deployer = Account(address=Address(deployer_pubkey, DEFAULT_HRP)) + deployer = Account(address=Address(deployer_pubkey, get_address_hrp())) deployer.nonce = 0 address_computer = AddressComputer(number_of_shards=3) contract_address = address_computer.compute_contract_address(deployer.address, deployer.nonce) diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index b5cb32b4..2164ad6d 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -182,3 +182,8 @@ def __init__(self, message: str, inner: Any = None): class NativeAuthClientError(KnownError): def __init__(self, message: str): super().__init__(message) + + +class IncorrectWalletError(KnownError): + def __init__(self, message: str): + super().__init__(message) diff --git a/multiversx_sdk_cli/interfaces.py b/multiversx_sdk_cli/interfaces.py index e0db9a2f..469047a0 100644 --- a/multiversx_sdk_cli/interfaces.py +++ b/multiversx_sdk_cli/interfaces.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Protocol, Sequence +from typing import Any, Dict, Protocol class IAddress(Protocol): @@ -26,10 +26,7 @@ class ITransaction(Protocol): signature: bytes guardian_signature: bytes relayer: str - - @property - def inner_transactions(self) -> Sequence["ITransaction"]: - ... + relayer_signature: bytes class IAccount(Protocol): diff --git a/multiversx_sdk_cli/localnet/step_config.py b/multiversx_sdk_cli/localnet/step_config.py index ba3a4447..48053a63 100644 --- a/multiversx_sdk_cli/localnet/step_config.py +++ b/multiversx_sdk_cli/localnet/step_config.py @@ -171,17 +171,13 @@ def copy_config_to_proxy(config: ConfigRoot): proxy_config = config.proxy_config_folder() makefolder(proxy_config) - shutil.copy( - config_prototype / 'config.toml', - proxy_config) + shutil.copy(config_prototype / 'config.toml', proxy_config) + shutil.copytree(config_prototype / 'apiConfig', proxy_config / 'apiConfig') - shutil.copytree( - config_prototype / 'apiConfig', - proxy_config / 'apiConfig') - - shutil.copy( - config_prototype / 'external.toml', - proxy_config) + # Removed in newer versions: + # https://github.com/multiversx/mx-chain-proxy-go/pull/454 + if (config_prototype / 'external.toml').exists(): + shutil.copy(config_prototype / 'external.toml', proxy_config) def patch_proxy_config(config: ConfigRoot): diff --git a/multiversx_sdk_cli/sign_verify.py b/multiversx_sdk_cli/sign_verify.py index 09b45a55..77e4c77e 100644 --- a/multiversx_sdk_cli/sign_verify.py +++ b/multiversx_sdk_cli/sign_verify.py @@ -23,7 +23,7 @@ def verify_signature(self) -> bool: verifiable_message.signature = bytes.fromhex(self.signature) message_computer = MessageComputer() - verifier = UserVerifier.from_address(Address.from_bech32(self.address)) + verifier = UserVerifier.from_address(Address.new_from_bech32(self.address)) is_signed = verifier.verify(message_computer.compute_bytes_for_signing(verifiable_message), verifiable_message.signature) return is_signed @@ -37,4 +37,4 @@ def to_dictionary(self) -> Dict[str, str]: def sign_message(message: str, account: Account) -> SignedMessage: signature = account.sign_message(message.encode()) - return SignedMessage(account.address.bech32(), message, signature) + return SignedMessage(account.address.to_bech32(), message, signature) diff --git a/multiversx_sdk_cli/tests/conftest.py b/multiversx_sdk_cli/tests/conftest.py new file mode 100644 index 00000000..abff81f5 --- /dev/null +++ b/multiversx_sdk_cli/tests/conftest.py @@ -0,0 +1,10 @@ +import pytest + + +# function executed right after test items collected but before test run +def pytest_collection_modifyitems(config, items): + if not config.getoption('-m'): + skip_me = pytest.mark.skip(reason="require_localnet will only run if explicitly set to with -m") + for item in items: + if "require_localnet" in item.keywords: + item.add_marker(skip_me) diff --git a/multiversx_sdk_cli/tests/test_cli_all.sh b/multiversx_sdk_cli/tests/test_cli_all.sh deleted file mode 100644 index 28cb34f2..00000000 --- a/multiversx_sdk_cli/tests/test_cli_all.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -USE_PROXY=$1 - -testAll() { - pushd $SCRIPT_DIR - source ./shared.sh - source ./test_cli_contracts.sh && testAll - source ./test_cli_dns.sh && testAll - - if [ -n "$USE_PROXY" ]; then - source ./test_cli_validators.sh && testAll - source ./test_cli_tx.sh && testAll - source ./test_cli_config.sh && testAll - fi - popd -} diff --git a/multiversx_sdk_cli/tests/test_cli_config.sh b/multiversx_sdk_cli/tests/test_cli_config.sh deleted file mode 100644 index d6609f52..00000000 --- a/multiversx_sdk_cli/tests/test_cli_config.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -source "./shared.sh" - -testAll() { - set -x - - ${CLI} --verbose config set proxy "https://testnet-api.multiversx.com" - ${CLI} --verbose config get proxy - ${CLI} --verbose config set txVersion 1 - ${CLI} --verbose config get txVersion - - set +x -} diff --git a/multiversx_sdk_cli/tests/test_cli_dns.sh b/multiversx_sdk_cli/tests/test_cli_dns.sh deleted file mode 100644 index fa3c9340..00000000 --- a/multiversx_sdk_cli/tests/test_cli_dns.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env bash - -source "./shared.sh" - -REGISTRATION_COST=100 - -testOnline() { - testRegistrationOnline || return 1 - # Wait for nonces to be incremented (at source shards) - sleep 15 - testTransactionsWithUsernamesOnline || return 1 -} - -testRegistrationOnline() { - ${CLI} --verbose dns register --name="testuser" --pem=${TestUser} --value=${REGISTRATION_COST} \ - --recall-nonce --gas-limit=100000000 --gas-price=1000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txRegisterUser.txt --send --proxy=${PROXY} || return 1 - - ${CLI} --verbose dns register --name="testuser2" --pem=${TestUser2} --value=${REGISTRATION_COST} \ - --recall-nonce --gas-limit=100000000 --gas-price=1000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txRegisterUser2.txt --send --proxy=${PROXY} || return 1 -} - -testTransactionsWithUsernamesOnline() { - ${CLI} --verbose tx new --pem=${TestUser} --receiver=${TestUser2} \ - --value="1${DENOMINATION}" --recall-nonce --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txA.txt --send --proxy=${PROXY} || return 1 - - sleep 10 - - ${CLI} --verbose tx new --pem=${TestUser} --receiver=${TestUser2} --receiver-username="testuser2" \ - --value="1${DENOMINATION}" --recall-nonce --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txB.txt --send --proxy=${PROXY} || return 1 - - sleep 10 - - ${CLI} --verbose tx new --pem=${TestUser} --receiver=${TestUser2} --receiver-username="testuser2foo" \ - --value="1${DENOMINATION}" --recall-nonce --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txC.txt --send --proxy=${PROXY} || return 1 - - sleep 10 - - ${CLI} --verbose tx new --pem=${TestUser} --sender-username="testuser" --receiver=${TestUser2} --receiver-username="testuser2" \ - --value="1${DENOMINATION}" --recall-nonce --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txD.txt --send --proxy=${PROXY} || return 1 - - sleep 10 - - ${CLI} --verbose tx new --pem=${TestUser} --sender-username="testuser" --receiver=${TestUser2} \ - --value="1${DENOMINATION}" --recall-nonce --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txF.txt --send --proxy=${PROXY} || return 1 - - sleep 10 - - ${CLI} --verbose tx new --pem=${TestUser} --sender-username="testuserfoo" --receiver=${TestUser2} \ - --value="1${DENOMINATION}" --recall-nonce --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txG.txt --send --proxy=${PROXY} || return 1 -} - - -testOffline() { - testRegistrationOffline || return 1 - testTransactionsWithUsernamesOffline || return 1 -} - -testRegistrationOffline() { - ${CLI} --verbose dns register --name="testuser" --pem=${TestUser} --value=${REGISTRATION_COST} \ - --nonce=7 --gas-limit=100000000 --gas-price=1000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txRegisterUser.txt || return 1 - assertFileExists ${SANDBOX}/txRegisterUser.txt || return 1 - - ${CLI} --verbose dns register --name="testuser2" --pem=${TestUser2} --value=${REGISTRATION_COST} \ - --nonce=8 --gas-limit=100000000 --gas-price=1000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txRegisterUser2.txt || return 1 - assertFileExists ${SANDBOX}/txRegisterUser2.txt || return 1 -} - -testTransactionsWithUsernamesOffline() { - ${CLI} --verbose tx new --pem=${TestUser} --receiver="erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" \ - --value="1${DENOMINATION}" --nonce=42 --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txA.txt || return 1 - assertFileExists ${SANDBOX}/txA.txt || return 1 - - ${CLI} --verbose tx new --pem=${TestUser} --receiver="erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" --receiver-username="testuser2" \ - --value="1${DENOMINATION}" --nonce=43 --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txB.txt || return 1 - assertFileExists ${SANDBOX}/txB.txt || return 1 - - ${CLI} --verbose tx new --pem=${TestUser} --receiver="erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" --receiver-username="testuser2foo" \ - --value="1${DENOMINATION}" --nonce=44 --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txC.txt || return 1 - assertFileExists ${SANDBOX}/txC.txt || return 1 - - ${CLI} --verbose tx new --pem=${TestUser} --sender-username="testuser" --receiver="erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" --receiver-username="testuser2" \ - --value="1${DENOMINATION}" --nonce=45 --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txD.txt || return 1 - assertFileExists ${SANDBOX}/txD.txt || return 1 - - ${CLI} --verbose tx new --pem=${TestUser} --sender-username="testuser" --receiver="erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" \ - --value="1${DENOMINATION}" --nonce=46 --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txF.txt || return 1 - assertFileExists ${SANDBOX}/txF.txt || return 1 - - ${CLI} --verbose tx new --pem=${TestUser} --sender-username="testuserfoo" --receiver="erd1ssmsc9022udc8pdw7wk3hxw74jr900xg28vwpz3z60gep66fasasl2nkm4" \ - --value="1${DENOMINATION}" --nonce=47 --gas-limit=50000 --gas-price=2000000000 --chain=${CHAIN_ID} \ - --outfile=${SANDBOX}/txG.txt || return 1 - assertFileExists ${SANDBOX}/txG.txt || return 1 -} - -testAll() { - testOnline || return 1 - testOffline || return 1 -} diff --git a/multiversx_sdk_cli/tests/test_cli_transactions.py b/multiversx_sdk_cli/tests/test_cli_transactions.py index c8148ae3..b7bcd4a3 100644 --- a/multiversx_sdk_cli/tests/test_cli_transactions.py +++ b/multiversx_sdk_cli/tests/test_cli_transactions.py @@ -1,7 +1,6 @@ import json -import os from pathlib import Path -from typing import Any, List +from typing import Any from multiversx_sdk_cli.cli import main @@ -89,92 +88,129 @@ def test_create_multi_transfer_transaction(capsys: Any): assert signature == "575b029d52ff5ffbfb7bab2f04052de88a6f7d022a6ad368459b8af9acaed3717d3f95db09f460649a8f405800838bc2c432496bd03c9039ea166bd32b84660e" -def test_create_and_save_inner_transaction(): +def test_create_multi_transfer_transaction_with_single_egld_transfer(capsys: Any): return_code = main([ "tx", "new", "--pem", str(testdata_path / "alice.pem"), "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - "--nonce", "77", - "--gas-limit", "500000", - "--relayer", "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8", - "--inner-transactions-outfile", str(testdata_out / "inner_transactions.json"), + "--nonce", "7", + "--gas-limit", "1300000", + "--token-transfers", "EGLD-000000", "1000000000000000000", "--chain", "T", ]) - assert False if return_code else True - assert Path(testdata_out / "inner_transactions.json").is_file() + assert return_code == 0 + tx = _read_stdout(capsys) + tx_json = json.loads(tx) + data = tx_json["emittedTransactionData"] + assert data == "MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@01@45474c442d303030303030@@0de0b6b3a7640000" -def test_create_and_append_inner_transaction(): +def test_relayed_v3_without_relayer_wallet(capsys: Any): return_code = main([ "tx", "new", "--pem", str(testdata_path / "alice.pem"), - "--receiver", "erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan", - "--nonce", "1234", - "--gas-limit", "50000", - "--relayer", "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8", - "--inner-transactions-outfile", str(testdata_out / "inner_transactions.json"), + "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", "7", + "--gas-limit", "1300000", + "--value", "1000000000000000000", "--chain", "T", + "--relayer", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" ]) - assert False if return_code else True + assert return_code == 0 + tx = _read_stdout(capsys) + tx_json = json.loads(tx)["emittedTransaction"] + assert tx_json["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx_json["receiver"] == "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" + assert tx_json["relayer"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert tx_json["signature"] + assert not tx_json["relayerSignature"] - with open(testdata_out / "inner_transactions.json", "r") as file: - json_file = json.load(file) - inner_txs: List[Any] = json_file["innerTransactions"] - assert len(inner_txs) == 2 +def test_relayed_v3_incorrect_relayer(): + return_code = main([ + "tx", "new", + "--pem", str(testdata_path / "alice.pem"), + "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", "7", + "--gas-limit", "1300000", + "--value", "1000000000000000000", + "--chain", "T", + "--relayer", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--relayer-pem", str(testdata_path / "alice.pem") + ]) + assert return_code -def test_create_invalid_relayed_transaction(): +def test_create_relayed_v3_transaction(capsys: Any): + # create relayed v3 tx and save signature and relayer signature + # create the same tx, save to file + # sign from file with relayer wallet and make sure signatures match return_code = main([ "tx", "new", - "--pem", str(testdata_path / "testUser.pem"), - "--receiver", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", - "--nonce", "987", - "--gas-limit", "5000000", - "--inner-transactions", str(testdata_out / "inner_transactions.json"), - "--data", "test data", + "--pem", str(testdata_path / "alice.pem"), + "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", "7", + "--gas-limit", "1300000", + "--value", "1000000000000000000", "--chain", "T", + "--relayer", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--relayer-pem", str(testdata_path / "testUser.pem") ]) - assert return_code + assert return_code == 0 + + tx = _read_stdout(capsys) + tx_json = json.loads(tx)["emittedTransaction"] + assert tx_json["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx_json["receiver"] == "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" + assert tx_json["relayer"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" + assert tx_json["signature"] + assert tx_json["relayerSignature"] + initial_sender_signature = tx_json["signature"] + initial_relayer_signature = tx_json["relayerSignature"] -def test_create_relayer_transaction(capsys: Any): + # Clear the captured content + capsys.readouterr() + + # save tx to file then load and sign tx by relayer return_code = main([ "tx", "new", - "--pem", str(testdata_path / "testUser.pem"), - "--receiver", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", - "--nonce", "987", - "--gas-limit", "5000000", - "--inner-transactions", str(testdata_out / "inner_transactions.json"), + "--pem", str(testdata_path / "alice.pem"), + "--receiver", "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "--nonce", "7", + "--gas-limit", "1300000", + "--value", "1000000000000000000", "--chain", "T", + "--relayer", "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5", + "--outfile", str(testdata_out / "relayed.json") ]) - # remove test file to ensure consistency when running test file locally - os.remove(testdata_out / "inner_transactions.json") + assert return_code == 0 - assert False if return_code else True + # Clear the captured content + capsys.readouterr() + + return_code = main([ + "tx", "relay", + "--relayer-pem", str(testdata_path / "testUser.pem"), + "--infile", str(testdata_out / "relayed.json") + ]) + assert return_code == 0 tx = _read_stdout(capsys) tx_json = json.loads(tx)["emittedTransaction"] + assert tx_json["signature"] == initial_sender_signature + assert tx_json["relayerSignature"] == initial_relayer_signature + + # Clear the captured content + capsys.readouterr() - assert tx_json["sender"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" - assert tx_json["receiver"] == "erd1cqqxak4wun7508e0yj9ng843r6hv4mzd0hhpjpsejkpn9wa9yq8sj7u2u5" - assert tx_json["gasLimit"] == 5000000 - assert tx_json["nonce"] == 987 - assert tx_json["chainID"] == "T" - - # should be the two inner transactions created in the tests above - inner_transactions = tx_json["innerTransactions"] - assert len(inner_transactions) == 2 - - assert inner_transactions[0]["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" - assert inner_transactions[0]["receiver"] == "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" - assert inner_transactions[0]["nonce"] == 77 - assert inner_transactions[0]["relayer"] == "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8" - - assert inner_transactions[1]["sender"] == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" - assert inner_transactions[1]["receiver"] == "erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan" - assert inner_transactions[1]["nonce"] == 1234 - assert inner_transactions[1]["relayer"] == "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8" + +def test_check_relayer_wallet_is_provided(): + return_code = main([ + "tx", "relay", + "--infile", str(testdata_out / "relayed.json") + ]) + assert return_code def _read_stdout(capsys: Any) -> str: diff --git a/multiversx_sdk_cli/tests/test_cli_tx.sh b/multiversx_sdk_cli/tests/test_cli_tx.sh deleted file mode 100644 index 618073bb..00000000 --- a/multiversx_sdk_cli/tests/test_cli_tx.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -source "./shared.sh" - -BOB="erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" - -testAll() { - cleanSandbox || return 1 - - echo "tx new, don't --send" - ${CLI} --verbose tx new --pem="${USERS}/alice.pem" --receiver=${BOB} --proxy=${PROXY} --value="1${DENOMINATION}" --recall-nonce --data="foo" --gas-limit=70000 --chain=${CHAIN_ID} --outfile=${SANDBOX}/txA.txt || return 1 - echo "tx send" - ${CLI} --verbose tx send --infile=${SANDBOX}/txA.txt --proxy=${PROXY} || return 1 - echo "tx new --send" - ${CLI} --verbose tx new --pem="${USERS}/bob.pem" --receiver=${BOB} --value="1${DENOMINATION}" --recall-nonce --data="foo" --gas-limit=60000 --chain=${CHAIN_ID} --send --outfile=${SANDBOX}/txB.txt --proxy=${PROXY} || return 1 - echo "tx new with --data-file" - echo '"{hello world!}"' > ${SANDBOX}/dummy.txt - ${CLI} --verbose tx new --pem="${USERS}/carol.pem" --receiver=${BOB} --value="1${DENOMINATION}" --recall-nonce --data-file=${SANDBOX}/dummy.txt --gas-limit=70000 --chain=${CHAIN_ID} --proxy=${PROXY} || return 1 - - echo "tx new --relay" - ${CLI} --verbose tx new --pem="${USERS}/dan.pem" --receiver=${BOB} --proxy=${PROXY} --value="1${DENOMINATION}" --nonce=1 --data="foo" --gas-limit=70000 --chain=${CHAIN_ID} --outfile=${SANDBOX}/txInner.txt --relay || return 1 - ${CLI} --verbose tx new --pem="${USERS}/eve.pem" --receiver=${BOB} --proxy=${PROXY} --value="1${DENOMINATION}" --recall-nonce --data-file=${SANDBOX}/txInner.txt --gas-limit=200000 --chain=${CHAIN_ID} --outfile=${SANDBOX}/txWrapper.txt || return 1 - - echo "tx new --simulate" - ${CLI} --verbose tx new --simulate --pem="${USERS}/frank.pem" --receiver=${BOB} --value="1${DENOMINATION}" --recall-nonce --data="foo" --gas-limit=70000 --chain=${CHAIN_ID} --proxy=${PROXY} || return 1 - - echo "tx new --send --wait-result" - ${CLI} --verbose tx new --send --wait-result --pem="${USERS}/grace.pem" --receiver=${BOB} --value="1${DENOMINATION}" --recall-nonce --data="foo" --gas-limit=70000 --chain=${CHAIN_ID} --proxy=${PROXY} || return 1 -} diff --git a/multiversx_sdk_cli/tests/test_cli_validators.py b/multiversx_sdk_cli/tests/test_cli_validators.py new file mode 100644 index 00000000..8b7cc521 --- /dev/null +++ b/multiversx_sdk_cli/tests/test_cli_validators.py @@ -0,0 +1,198 @@ +import pytest + +from pathlib import Path + +from multiversx_sdk_cli.cli import main + +testdata_path = Path(__file__).parent / "testdata" +testdata_out = Path(__file__).parent / "testdata-out" + +proxy_url = "http://localhost:7950/network/config" +alice_pem = testdata_path / "alice.pem" +reward_address = "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8" +bls_key = "e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208" + + +@pytest.mark.require_localnet +def test_stake(): + validators_json = testdata_path / "validators_ci.json" + + # Stake with recall nonce + return_code = main([ + "validator", "stake", + "--pem", str(alice_pem), + "--value", "2500000000000000000000", + "--validators-file", str(validators_json), + "--reward-address", reward_address, + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + # Stake with provided nonce + return_code = main([ + "validator", "stake", + "--pem", str(alice_pem), + "--value", "2500000000000000000000", + "--validators-file", str(validators_json), + "--reward-address", reward_address, + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--nonce=0" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_stake_top_up(): + # Stake with topUp + return_code = main([ + "validator", "stake", "--top-up", + "--pem", str(alice_pem), + "--value", "2711000000000000000000", + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_unstake(): + # Unstake + return_code = main([ + "validator", "unstake", + "--pem", str(alice_pem), + "--nodes-public-key", bls_key, + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_unbond(): + # Unbond + return_code = main([ + "validator", "unbond", + "--pem", str(alice_pem), + "--nodes-public-key", bls_key, + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_unjail(): + # Unjail + return_code = main([ + "validator", "unjail", + "--pem", str(alice_pem), + "--value", "2500000000000000000000", + "--nodes-public-key", bls_key, + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_change_reward_address(): + # Change reward address + return_code = main([ + "validator", "change-reward-address", + "--pem", str(alice_pem), + "--reward-address", reward_address, + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_unstake_nodes(): + # Unstake Nodes + return_code = main([ + "validator", "unstake-nodes", + "--pem", str(alice_pem), + "--nodes-public-key", bls_key, + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_unstake_tokens(): + # Unstake Tokens + return_code = main([ + "validator", "unstake-tokens", + "--pem", str(alice_pem), + "--unstake-value", "11000000000000000000", + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_unbond_nodes(): + # Unbond nodes + return_code = main([ + "validator", "unbond-nodes", + "--pem", str(alice_pem), + "--nodes-public-keys", bls_key, + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_unbond_tokens(): + # Unbond nodes + return_code = main([ + "validator", "unbond-tokens", + "--pem", str(alice_pem), + "--unbond-value", "20000000000000000000", + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_clean_registration_data(): + # Clean registration data + return_code = main([ + "validator", "clean-registered-data", + "--pem", str(alice_pem), + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 + + +@pytest.mark.require_localnet +def test_re_stake_unstaked_nodes(): + # Clean registration data + return_code = main([ + "validator", "restake-unstaked-nodes", + "--pem", str(alice_pem), + "--nodes-public-keys", bls_key, + "--chain", "localnet", + "--proxy", "http://127.0.0.1:7950", + "--estimate-gas", "--recall-nonce" + ]) + assert return_code == 0 diff --git a/multiversx_sdk_cli/tests/test_cli_validators.sh b/multiversx_sdk_cli/tests/test_cli_validators.sh deleted file mode 100755 index d489c9c0..00000000 --- a/multiversx_sdk_cli/tests/test_cli_validators.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -source "./shared.sh" - -testAll() { - BLS_KEY="e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208" - REWARD_ADDRESS="erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8" - - echo "Stake with recall nonce" - ${CLI} --verbose validator stake --pem="${USERS}/alice.pem" --value="2500${DENOMINATION}" --validators-file=./testdata/validators.json --reward-address=${REWARD_ADDRESS} --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - echo "Stake with provided nonce" - ${CLI} --verbose validator stake --pem="${USERS}/bob.pem" --value="2500${DENOMINATION}" --validators-file=./testdata/validators.json --reward-address=${REWARD_ADDRESS} --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --nonce=300 || return 1 - - - echo "Stake with topUP" - ${CLI} --verbose validator stake --top-up --pem="${USERS}/carol.pem" --value="2711${DENOMINATION}" --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - - echo "Unstake" - ${CLI} --verbose validator unstake --pem="${USERS}/dan.pem" --nodes-public-keys="${BLS_KEY}" --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - echo "Unbond" - ${CLI} --verbose validator unbond --pem="${USERS}/eve.pem" --nodes-public-keys=${BLS_KEY} --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - echo "Unjail" - ${CLI} --verbose validator unjail --pem="${USERS}/frank.pem" --value="2500${DENOMINATION}" --nodes-public-keys=${BLS_KEY} --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - echo "Change reward address" - ${CLI} --verbose validator change-reward-address --pem="${USERS}/grace.pem" --reward-address=${REWARD_ADDRESS} --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - - echo "UnstakeNodes" - ${CLI} --verbose validator unstake-nodes --pem="${USERS}/heidi.pem" --nodes-public-keys=${BLS_KEY} --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - - echo "UnstakeTokens" - ${CLI} --verbose validator unstake-tokens --pem="${USERS}/ivan.pem" --unstake-value="11${DENOMINATION}" --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - - echo "UnbondNodes" - ${CLI} --verbose validator unbond-nodes --pem="${USERS}/judy.pem" --nodes-public-keys=${BLS_KEY} --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - - echo "UnbondTokens" - ${CLI} --verbose validator unbond-tokens --pem="${USERS}/mallory.pem" --unbond-value="20${DENOMINATION}" --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - - echo "CleanRegistrationData" - ${CLI} --verbose validator clean-registered-data --pem="${USERS}/mike.pem" --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 - - echo "ReStakeUnstakedNodes" - ${CLI} --verbose validator restake-unstaked-nodes --pem="${USERS}/alice.pem" --nodes-public-keys=${BLS_KEY} --chain=${CHAIN_ID} --proxy=${PROXY} --estimate-gas --recall-nonce || return 1 -} diff --git a/multiversx_sdk_cli/tests/test_proxy.py b/multiversx_sdk_cli/tests/test_proxy.py index 8cff09a5..8234f7fb 100644 --- a/multiversx_sdk_cli/tests/test_proxy.py +++ b/multiversx_sdk_cli/tests/test_proxy.py @@ -2,6 +2,7 @@ from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.cli import main +from multiversx_sdk_cli.config import get_config_for_network_providers def test_get_account(): @@ -20,7 +21,8 @@ def test_get_account(): def test_sync_nonce(): account = Account(address=Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th")) - proxy = ProxyNetworkProvider("https://testnet-api.multiversx.com") + config = get_config_for_network_providers() + proxy = ProxyNetworkProvider("https://testnet-api.multiversx.com", config=config) account.sync_nonce(proxy) assert True if account.nonce else False diff --git a/multiversx_sdk_cli/tests/testdata/validators_ci.json b/multiversx_sdk_cli/tests/testdata/validators_ci.json new file mode 100644 index 00000000..99a390c6 --- /dev/null +++ b/multiversx_sdk_cli/tests/testdata/validators_ci.json @@ -0,0 +1,13 @@ +{ + "validators": [ + { + "pemFile": "~/work/mx-sdk-py-cli/mx-sdk-py-cli/localnet/validator00/config/validatorKey.pem" + }, + { + "pemFile": "~/work/mx-sdk-py-cli/mx-sdk-py-cli/localnet/validator01/config/validatorKey.pem" + }, + { + "pemFile": "~/work/mx-sdk-py-cli/mx-sdk-py-cli/localnet/validator02/config/validatorKey.pem" + } + ] +} diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 1e441a17..2c58c4a7 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -2,20 +2,19 @@ import json import logging import time -from pathlib import Path -from typing import Any, Dict, List, Optional, Protocol, TextIO +from typing import Any, Dict, List, Optional, Protocol, TextIO, Union from multiversx_sdk import (Address, Token, TokenComputer, TokenTransfer, Transaction, TransactionsConverter, TransactionsFactoryConfig, - TransferTransactionsFactory) + TransferTransactionsFactory, TransactionComputer) from multiversx_sdk_cli import errors -from multiversx_sdk_cli.accounts import Account, LedgerAccount +from multiversx_sdk_cli.accounts import Account, AccountBase, LedgerAccount from multiversx_sdk_cli.cli_password import (load_guardian_password, load_password) from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.errors import NoWalletProvided +from multiversx_sdk_cli.errors import IncorrectWalletError, NoWalletProvided from multiversx_sdk_cli.interfaces import ITransaction from multiversx_sdk_cli.ledger.ledger_functions import do_get_ledger_address @@ -76,17 +75,40 @@ def do_prepare_transaction(args: Any) -> Transaction: tx.version = int(args.version) tx.options = int(args.options) + tx_computer = TransactionComputer() + if isinstance(account, LedgerAccount): + tx_computer.apply_options_for_hash_signing(tx) + if args.guardian: tx.guardian = args.guardian if args.relayer: - tx.relayer = Address.new_from_bech32(args.relayer).to_bech32() + tx.relayer = args.relayer + + try: + relayer_account = load_relayer_account_from_args(args) + if relayer_account.address.to_bech32() != tx.relayer: + raise IncorrectWalletError("") - if args.inner_transactions: - tx.inner_transactions = load_inner_transactions_from_file(Path(args.inner_transactions).expanduser()) + if isinstance(relayer_account, LedgerAccount): + tx_computer.apply_options_for_hash_signing(tx) + + tx.relayer_signature = bytes.fromhex(relayer_account.sign_transaction(tx)) + except NoWalletProvided: + logger.warning("Relayer wallet not provided. Transaction will not be signed by relayer.") + except IncorrectWalletError: + raise IncorrectWalletError("Relayer wallet does not match the relayer's address set in the transaction.") + + try: + guardian_account = get_guardian_account_from_args(args) + if isinstance(guardian_account, LedgerAccount): + tx_computer.apply_options_for_hash_signing(tx) + + except NoWalletProvided: + guardian_account = None tx.signature = bytes.fromhex(account.sign_transaction(tx)) - tx = sign_tx_by_guardian(args, tx) + tx = sign_tx_by_guardian(args, tx, guardian_account) return tx @@ -104,6 +126,20 @@ def load_sender_account_from_args(args: Any) -> Account: return account +def load_relayer_account_from_args(args: Any) -> Account: + if args.relayer_ledger: + account = LedgerAccount(account_index=args.relayer_ledger_account_index, address_index=args.relayer_ledger_address_index) + if args.relayer_pem: + account = Account(pem_file=args.relayer_pem, pem_index=args.relayer_pem_index) + elif args.relayer_keyfile: + password = load_password(args) + account = Account(key_file=args.relayer_keyfile, password=password) + else: + raise errors.NoWalletProvided() + + return account + + def prepare_token_transfers(transfers: List[Any]) -> List[TokenTransfer]: token_computer = TokenComputer() token_transfers: List[TokenTransfer] = [] @@ -120,12 +156,7 @@ def prepare_token_transfers(transfers: List[Any]) -> List[TokenTransfer]: return token_transfers -def sign_tx_by_guardian(args: Any, tx: Transaction) -> Transaction: - try: - guardian_account = get_guardian_account_from_args(args) - except NoWalletProvided: - guardian_account = None - +def sign_tx_by_guardian(args: Any, tx: Transaction, guardian_account: Union[AccountBase, None]) -> Transaction: if guardian_account: tx.guardian_signature = bytes.fromhex(guardian_account.sign_transaction(tx)) elif args.guardian: @@ -143,7 +174,7 @@ def get_guardian_account_from_args(args: Any): account = Account(key_file=args.guardian_keyfile, password=password) elif args.guardian_ledger: address = do_get_ledger_address(account_index=args.guardian_ledger_account_index, address_index=args.guardian_ledger_address_index) - account = Account(address=Address.from_bech32(address)) + account = Account(address=Address.new_from_bech32(address)) else: raise errors.NoWalletProvided() @@ -227,11 +258,3 @@ def load_transaction_from_file(f: TextIO) -> Transaction: tx_converter = TransactionsConverter() return tx_converter.dictionary_to_transaction(transaction_dictionary) - - -def load_inner_transactions_from_file(path: Path) -> List[Transaction]: - data_json = path.read_text() - transactions: List[Dict[str, Any]] = json.loads(data_json).get("innerTransactions") - - tx_converter = TransactionsConverter() - return [tx_converter.dictionary_to_transaction(transaction) for transaction in transactions] diff --git a/multiversx_sdk_cli/validators/core.py b/multiversx_sdk_cli/validators/core.py index 0e6e9f95..f71d64ee 100644 --- a/multiversx_sdk_cli/validators/core.py +++ b/multiversx_sdk_cli/validators/core.py @@ -8,14 +8,14 @@ from multiversx_sdk_cli.accounts import Account from multiversx_sdk_cli.cli_password import load_password from multiversx_sdk_cli.config import (GAS_PER_DATA_BYTE, MIN_GAS_LIMIT, - MetaChainSystemSCsCost) + MetaChainSystemSCsCost, get_address_hrp) from multiversx_sdk_cli.contracts import prepare_execute_transaction_data from multiversx_sdk_cli.errors import BadUsage from multiversx_sdk_cli.validators.validators_file import ValidatorsFile logger = logging.getLogger("validators") -VALIDATORS_SMART_CONTRACT_ADDRESS = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplllst77y4l" +VALIDATORS_SMART_CONTRACT_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000001ffff" def prepare_args_for_stake(args: Any): @@ -36,7 +36,7 @@ def prepare_args_for_stake(args: Any): data, gas_limit = prepare_transaction_data_for_stake(node_operator.address, validators_file_path, reward_address) args.data = data - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = gas_limit @@ -74,7 +74,7 @@ def prepare_transaction_data_for_stake(node_operator_address: Address, validator def prepare_args_for_top_up(args: Any): args.data = 'stake' - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.STAKE, 1) @@ -83,7 +83,7 @@ def prepare_args_for_top_up(args: Any): def prepare_args_for_unstake(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) args.data = 'unStake' + parsed_keys - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNSTAKE, num_keys) @@ -92,7 +92,7 @@ def prepare_args_for_unstake(args: Any): def prepare_args_for_unbond(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) args.data = 'unBond' + parsed_keys - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNBOND, num_keys) @@ -101,7 +101,7 @@ def prepare_args_for_unbond(args: Any): def prepare_args_for_unjail(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) args.data = 'unJail' + parsed_keys - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNJAIL, num_keys) @@ -110,7 +110,7 @@ def prepare_args_for_unjail(args: Any): def prepare_args_for_change_reward_address(args: Any): reward_address = Address.new_from_bech32(args.reward_address) args.data = 'changeRewardAddress@' + reward_address.hex() - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.CHANGE_REWARD_ADDRESS) @@ -118,7 +118,7 @@ def prepare_args_for_change_reward_address(args: Any): def prepare_args_for_claim(args: Any): args.data = 'claim' - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.CLAIM) @@ -128,7 +128,7 @@ def prepare_args_for_unstake_nodes(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) args.data = 'unStakeNodes' + parsed_keys - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNSTAKE, num_keys) @@ -137,7 +137,7 @@ def prepare_args_for_unstake_tokens(args: Any): args.data = 'unStakeTokens' args.data += '@' + utils.str_int_to_hex_str(str(args.unstake_value)) - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNSTAKE_TOKENS) @@ -146,7 +146,7 @@ def prepare_args_for_unbond_nodes(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) args.data = 'unBondNodes' + parsed_keys - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNBOND, num_keys) @@ -155,7 +155,7 @@ def prepare_args_for_unbond_tokens(args: Any): args.data = 'unBondTokens' args.data += '@' + utils.str_int_to_hex_str(str(args.unbond_value)) - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.UNBOND_TOKENS) @@ -163,7 +163,7 @@ def prepare_args_for_unbond_tokens(args: Any): def prepare_args_for_clean_registered_data(args: Any): args.data = 'cleanRegisteredData' - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.STAKE) @@ -172,7 +172,7 @@ def prepare_args_for_restake_unstaked_nodes(args: Any): parsed_keys, num_keys = utils.parse_keys(args.nodes_public_keys) args.data = 'reStakeUnStakedNodes' + parsed_keys - args.receiver = VALIDATORS_SMART_CONTRACT_ADDRESS + args.receiver = Address.new_from_hex(VALIDATORS_SMART_CONTRACT_ADDRESS_HEX, get_address_hrp()).to_bech32() if args.estimate_gas: args.gas_limit = estimate_system_sc_call(args.data, MetaChainSystemSCsCost.STAKE, num_keys) diff --git a/pyproject.toml b/pyproject.toml index c90a4f59..a2fc4738 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "multiversx-sdk-cli" -version = "9.7.0" +version = "9.10.2" authors = [ { name="MultiversX" }, ] @@ -28,7 +28,7 @@ dependencies = [ "requests-cache", "rich==13.3.4", "argcomplete==3.2.2", - "multiversx-sdk==0.13.0" + "multiversx-sdk==0.19.0" ] [project.scripts] diff --git a/pytest.ini b/pytest.ini index 6669f27f..4cee0be1 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,7 @@ [pytest] markers = - skip_on_windows: marks tests as being skiped when running on windows (deselect with '-m "skip_on_windows"') + skip_on_windows: marks tests as being skipped when running on windows (deselect with '-m "skip_on_windows"') only: only run a specific test (run using: pytest -m "only") + require_localnet: marks tests that require a localnet (select with '-m "require_localnet"') log_cli = True diff --git a/requirements.txt b/requirements.txt index 2048553c..0c7334d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,4 @@ requests-cache rich==13.3.4 argcomplete==3.2.2 -multiversx-sdk==0.13.0 +multiversx-sdk==0.19.0