Skip to main content

mxpy CLI cookbook

mxpy (Command Line Interface)

mxpy, as a command-line tool, can be used to simplify and automate the interaction with the MultiversX network - it can be easily used in shell scripts, as well. It implements a set of commands, organized within groups.

The complete Command Line Interface is listed here. Command usage and description are available through the --help or -h flags.

For example:

mxpy --help
mxpy tx --help
mxpy tx new --help

This page will guide you through the process of handling common tasks using mxpy.

Upgrading mxpy

Upgrade using pipx

In case you used pipx to install mxpy, to upgrade to a newer version, you can run the following command:

pipx upgrade multiversx-sdk-cli

Using the installation script

This method should only be used if you installed mxpy as described here.

If you are using a older version of mxpy you can simply upgrade to a newer version by typing the following commands in a terminal:

wget -O mxpy-up.py https://raw.githubusercontent.com/multiversx/mx-sdk-py-cli/main/mxpy-up.py && python3 mxpy-up.py

This will recreate the light Python virtual environment (based on venv) in ~/multiversx-sdk/mxpy-venv. Since you've had a previous mxpy version installed, you probably have already altered the $PATH variable so you don't have to re-alter it.

Managing dependencies

Using mxpy you can either check if a dependency is installed or install a new dependency.

To check if a dependecy is installed you can use:

mxpy deps check <dependecy-name>

To install a new dependency you can use:

mxpy deps install <dependency-name>

Both mxpy deps check <dependecy-name> and mxpy deps install <dependency-name> use the <dependency-name> as a positional argument.

To find out which dependencies can be managed using mxpy you can type one of the following commands and you will see which positional arguments it accepts:

mxpy deps check -h
mxpy deps install -h

For example, in order to check if rust is installed you would type:

mxpy deps check rust

When installing dependecies the --overwrite argument can be used to overwrite an existing version.

For example, to install rust, you can simply type the command:

mxpy deps install rust

If no tag is provided the default version will be installed.

Default rust version

Generally speaking, the default rust version installed by mxpy is the one referenced by the latest Docker image used for reproducible builds.

Here's how to install a specific version of rust (example):

mxpy deps install rust --overwrite

Creating wallets

There are a couple available wallet formats:

  • raw-mnemonic - secret phrase in plain text
  • keystore-mnemonic - secret phrase, as a password-encrypted JSON keystore file
  • keystore-secret-key - secret key (irreversibly derived from the secret phrase), as a password-encrypted JSON keystore file
  • pem - secret key (irreversibly derived from the secret phrase), as a PEM file

For this example, we are going to create a keystore-mnemonic wallet.

Let's create a keystore wallet:

mxpy wallet new --format keystore-mnemonic --outfile test_wallet.json

The wallet's mnemonic will appear, followed by a prompt to set a password for the file. Once you input the password and press "Enter", the file will be generated at the location specified by the --outfile argument.

Converting a wallet

As you have read above, there are multiple ways in which you can store your secret keys.

To convert a wallet from a type to another you can use:

mxpy wallet convert
info

Keep in mind that the conversion isn't always possible (due to irreversible derivations of the secret phrase):

  • raw-mnemonic can be converted to any other format
  • keystore-mnemonic can be converted to any other format
  • keystore-secret-key can only be converted to pem
  • pem can only be converted to keystore-secret-key

It's mandatory that you keep a backup of your secret phrase somewhere safe.

Let's convert the previously created keystore-mnemonic to a PEM wallet. We discourage the use of PEM wallets for storing cryptocurrencies due to their lower security level. However, they prove to be highly convenient and user-friendly for application testing purposes.

To convert the wallet we type the follwing command:

mxpy wallet convert --infile test_wallet.json --in-format keystore-mnemonic --outfile converted_wallet.pem --out-format pem

After being prompted to enter the password you've previously set for the wallet the new .pem file will be created.

The command arguments can be found here or by typing:

mxpy wallet convert --help

Building a smart contract

In order to deploy a smart contract on the network, you need to build it first.

The contract we will be using for this examples can be found here.

The mxpy command used for building contracts is:

mxpy contract build --path <path to contract>

If our working directory is already the contract's directory we can skip the --path argument as by default the contract's directory is the current working directory.

The generated .wasm file will be created in a directory called output inside the contract's directory.

The command accepts a few parameters that you can check out here or by simply typing:

mxpy contract build --help

If you'd like to build a smart contract directly using sc-meta instead, please follow this.

Deploying a smart contract

After you've built your smart contract, it can be deployed on the network.

For deploying a smart contract the following command can be used:

mxpy contract deploy

To deploy a smart contract you have to send a transaction to the Smart Contract Deploy Address and that address is erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu, but you don't have to worry about setting the receiver of the transaction because the above command takes care of it.

The --bytecode argument specifies the path to your previously-built contract. If you've built the smart contract using mxpy, the generated .wasm file will be in a folder called output.

For example, if your contract is in ~/contracts/adder, the generated bytecode file adder.wasm will be in ~/contracts/adder/output. So, when providing the --bytecode argument the path should be ~/contracts/adder/output/adder.wasm.

The mxpy contract deploy command needs a multitude of other parameters that can be checked out here or by simply typing the following:

mxpy contract deploy --help

We will use a .pem file for the sake of simplicity but you can easily use any wallet type.

Let's see a simple example:

mxpy contract deploy --bytecode ~/contracts/adder/output/adder.wasm \
--proxy=https://devnet-gateway.multiversx.com --recall-nonce \
--arguments 0 --gas-limit 5000000 \
--pem=~/multiversx-sdk/testwallets/latest/users/alice.pem \
--send

The --proxy is used to specify the url of the proxy and the --chain is used to select the network the contract will be deployed to. The chain ID and the proxy need to match for our transaction to be executed. We can't prepare a transaction for the Devnet (using --chain D) and send it using the mainnet proxy (https://gateway.multiversx.com).

The --recall-nonce is used to get the nonce of the address so we don't search it manually. It simply makes an API request to get the nonce of the account. The --arguments is used in case our contract needs any arguments for the initialization. We know our adder needs a value to start adding from, so we set that to 0.

The --gas-limit is used to set the gas we are willing to pay so our transaction will be executed. 5 million gas is a bit too much because our contract is very small and simple, but better to be sure. In case our transaction doesn't have enough gas the network will not execute it, saying something like Insufficent gas limit.

The --pem argument is used to provide the sender of the transaction, the payer of the fee. The sender will also be the owner of the contract.

Calling the Smart Contract

After deploying our smart contract we can start interacting with it. The contract has a function called add() that we can call and it will increase the value stored in the contract with the value we provide.

To call a function we use the mxpy contract call command. Here's an example of how we can do that:

mxpy contract call erd1qqqqqqqqqqqqqpgq3zrpqj3sulnc9xq95sljetxhf9s07pqtd8ssfkxjv4 \
--pem=~/multiversx-sdk/testwallets/latest/users/alice.pem --recall-nonce \
--proxy=https://devnet-gateway.multiversx.com --chain D \
--function add --arguments 5 --gas-limit 1000000 \
--send

The positional argument is the contract address that we want to interact with. The --pem, --recall-nonce, --proxy and --chain arguments are used the same as above in the deploy transaction.

Using the --function argument we specify the function we want to call and with the --arguments argument we specify the value we want to add. We set the gas we are willing to pay for the transaction and finally we send the transaction.

Querying a Smart Contract

Querying a contract is done by calling a so called view function. We can get data from a contract without sending a transaction to the contract, basically without spending money.

As you know, our contract has a function called add() that we previously called, and a view function called getSum(). Using this getSum() function we can see the value that is currently stored in the contract.

If you remember, when we deployed the contract we passed the value 0 as a contract argument, this means the contract started adding from 0. When calling the add() function we used the value 5. This means that now if we call getSum() we should get the value 5. To do that, we use the mxpy contract query command. Let's try it!

mxpy contract query erd1qqqqqqqqqqqqqpgq3zrpqj3sulnc9xq95sljetxhf9s07pqtd8ssfkxjv4 \
--proxy https://devnet-gateway.multiversx.com \
--function getSum

We see that mxpy returns our value as a base64 string, as a hex number and as a integer. Indee, we see the expected value.

Upgrading a Smart Contract

In case there's a new release of your Smart Contract, or perhaps you've patched a possible vulnerability you can upgrade the code of the Smart Contract deployed on the network.

We've modified our adder contract to add 1 to every value added to the contract. Now everytime the add() function is called will add the value provided with 1. In order to do that we access the source code and navigate to the add() endpoint. We can see that value is added to sum each time the endpoint is called. Modify the line to look something like this self.sum().update(|sum| *sum += value + 1u32);

Before deploying the contract we need to build it again to make sure we are using the latest version. We then deploy the newly built contract, then we call it and query it.

First we build the contract:

mxpy contract build

Then we upgrade the contract by running:

mxpy contract upgrade erd1qqqqqqqqqqqqqpgq3zrpqj3sulnc9xq95sljetxhf9s07pqtd8ssfkxjv4 \
--bytecode ~/contracts/adder/output/adder.wasm \
--proxy=https://devnet-gateway.multiversx.com --chain D \
--recall-nonce --arguments 0 --gas-limit 5000000 \
--pem=~/multiversx-sdk/testwallets/latest/users/alice.pem \
--send

We provide as a positional argument the contract's address that we want to upgrade, in our case the previously deployed adder contract. The --bytecode is used to provide the new code that will replace the old code. We also set the --arguments to 0 as we didn't change the constructor and the contract will start counting from 0 again. The rest of the arguments you know from all the previous operations we've done.

Now let's add 5 to the contract one more time. We do so by running the following:

mxpy contract call erd1qqqqqqqqqqqqqpgq3zrpqj3sulnc9xq95sljetxhf9s07pqtd8ssfkxjv4 \
--pem=~/multiversx-sdk/testwallets/latest/users/alice.pem --recall-nonce \
--proxy=https://devnet-gateway.multiversx.com --chain D \
--function add --arguments 5 --gas-limit 1000000 \
--send

Now, if we query the contract we should see the value 6. We added 5 in the contract but modified the contract code to add 1 to every value. Let's see!

mxpy contract query erd1qqqqqqqqqqqqqpgq3zrpqj3sulnc9xq95sljetxhf9s07pqtd8ssfkxjv4 --proxy https://devnet-gateway.multiversx.com --function getSum

We see that we indeed got the value 6. Our upgrade was sucessfull.

Verifying a smart contract

Verifying a smart contract means ensuring that the contract deployed on the network matches a specific version of the original source code. That is done by an external service that, under the hood, performs a reproducible build of the given contract and compares the resulting bytecode with the one deployed on the network.

To learn more about reproducible builds, please follow this page. If you'd like to set up a Github Workflow that performs a reproducible build of your smart contract, follow the examples in this repository.

The command used for verifying contracts is:

mxpy contract verify

Let's see an example:

export CONTRACT_ADDRESS="erd1qqqqqqqqqqqqqpgq6eynj8xra5v87qqzpjhc5fnzzh0fqqzld8ssqrez2g"

mxpy --verbose contract verify ${CONTRACT_ADDRESS} \
--packaged-src=adder-0.0.0.source.json \
--verifier-url="https://devnet-play-api.multiversx.com" \
--docker-image="multiversx/sdk-rust-contract-builder:v5.1.0" \
--pem=~/multiversx-sdk/testwallets/latest/users/alice.pem
info

The account that triggers the code verification process must be the owner of the contract.

info

The packaged source passed as --packaged-src can be obtained either from the Github Workflows for reproducible builds set up on your own repository, or from locally invoking a reproducible build, as depicted here.

Creating and sending transactions

To create a new transaction we use the mxpy tx new command. Let's see how that works:

mxpy tx new --pem ~/multiversx-sdk/testwallets/latest/users/alice.pem --recall-nonce \
--receiver erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx \
--gas-limit 50000 --value 1000000000000000000 \
--proxy https://devnet-gateway.multiversx.com --chain D \
--send

That's it! As easy as that. We sent a transaction from Alice to Bob. We choose the receiver of our transaction using the --receiver argument and set the gas limit to 50000 because that is the gas cost of a simple move balance transaction. Notice we used the --value argument to pass the value that we want to transfer but we passed in the denomintated value. We transferred 1 eGLD (1 * 10^18). We then specify the proxy and the chain ID for the network we want to send our transaction to and use the --send argument to broadcast it.

In case you want to save the transaction you can also provide the --outfile argument and a json file containing the transaction will be saved at the specified location. If you just want to prepare the transaction without broadcasting it simply remove the --send argument.

Guarded transactions

If your address is guarded, you'll have to provide some additional arguments because your transaction needs to be co-signed.

The first extra argument we'll need is the --guardian argument. This specifies the guardian address of our address. Then, if our account is guarded by a service like our trusted co-signer service we have to provide the --guardian-service-url which specifies where the transaction is sent to be co-signed.

Keep in mind that mxpy always calls the /sign-transaction endpoint of the --guardian-service-url you have provided. Another argment we'll need is --guardian-2fa-code which is the code generated by an external authenticator.

Each guarded transaction needs an additional 50000 gas for the gasLimit. The version field needs to be set to 2. The options field needs to have the second least significant bit set to "1".

mxpy tx new --pem ~/multiversx-sdk/testwallets/latest/users/alice.pem --recall-nonce \
--receiver erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx \
--gas-limit 200000 --value 1000000000000000000 \
--proxy https://devnet-gateway.multiversx.com --chain D \
--guardian erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8 \
--guardian-service-url https://devnet-tools.multiversx.com/guardian \
--guardian-2fa-code 123456 --version 2 --options 2
--send

If your address is guarded by another wallet, you'll still need to provide the --guardian argument and the guardian's wallet that will co-sign the transaction, but you don't need to provide the 2fa code and the service url. You can provide the guardian's wallet using one of the following arguments: --guardian-pem, --guardian-keyfile, or --guardian-ledger.

Using the Ledger hardware wallet

You can sign any transaction (regular transfers, smart contract deployments and calls) using a Ledger hardware wallet by leveraging the --ledger command-line argument.

First, connect your device to the computer, unlock it and open the MultiversX Ledger app.

Then, you can perform a trivial connectivity check by running:

mxpy ledger version

The output should look like this:

MultiversX App version: ...

Another trivial check is to ask the device for the (first 10) MultiversX addresses it manages:

mxpy ledger addresses

The output should look like this:

account index = 0 | address index = 0 | address: erd1...
account index = 0 | address index = 1 | address: erd1...
account index = 0 | address index = 2 | address: erd1...
...

Now let's sign and broadcast a transaction (EGLD transfer):

mxpy tx new --proxy https://devnet-gateway.multiversx.com --recall-nonce \
--receiver erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th \
--gas-limit 50000 --value 1000000000000000000 \
--ledger \
--send

By default, the first MultiversX address managed by the device is used as the sender (signer) of the transaction. In order to select a different address, you can use the --ledger-address-index CLI parameter:

mxpy tx new --proxy https://devnet-gateway.multiversx.com --recall-nonce \
--receiver erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th \
--gas-limit 50000 --value 1000000000000000000 \
--ledger --ledger-address-index=42 \
--send
info

For MultiversX, the account index should always be 0, while the address index is allowed to vary. Therefore, you should not use the --ledger-account-index CLI parameter (it will be removed in a future release).

Now let's deploy a smart contract using the Ledger:

mxpy contract deploy --proxy=https://devnet-gateway.multiversx.com --recall-nonce \
--bytecode=counter.wasm --gas-limit=5000000 \
--ledger --ledger-address-index=42 \
--send

Then, perform a contract call:

mxpy contract call erd1qqqqqqqqqqqqqpgqwwef37kmegph97egvvrxh3nccx7xuygez8ns682zz0 \
--proxy=https://devnet-gateway.multiversx.com --recall-nonce \
--function increment --gas-limit 5000000 \
--ledger --ledger-address-index=42 \
--send
note

As of October 2023, on Windows (or WSL), you might encounter some issues when trying to use Ledger in mxpy.