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.

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 to see the 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

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

mxpy deps install rust

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

For example, to overwrite your current rust installation, you can simply type the command:

mxpy deps install rust --overwrite

If the configuration is not altered, 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.

note

On Ubuntu (or Windows with WSL), you might need to install the following dependencies of Rust and sc-meta before running mxpy deps install rust:

sudo apt-get install build-essential pkg-config libssl-dev

Configuring mxpy

The configuration can be altered using the mxpy config command.

tip

mxpy's configuration is stored in the file ~/multiversx-sdk/mxpy.json.

Viewing the current mxpy configuration

In order to view the current configuration, one can issue the command mxpy config dump. Output example:

{
"dependencies.rust.tag": ""
}

Updating the mxpy configuration

One can alter the current configuration using the command mxpy config set. For example, in order to set the rust version to be used, one would do the following:

$ mxpy config set dependencies.rust.tag stable

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 can be built using mxpy, but for a more granular approach, sc-meta should be used. To learn more about sc-meta, please check out this page.

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

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.

Deploying a smart contract providing the ABI file

For functions that have complex arguments, we can use the ABI file generated when building the contract. The ABI can be provided using the --abi argument. When using the ABI, and only when using the ABI, the arguments should be written in a json file and should be provided via the --arguments-file argument.

For this example, we'll use the multisig contract.

First, we'll prepare the file containing the constructors arguments. We'll refer to this file as deploy_multisig_arguments.json. The constructor requires two arguments, the first is of type u32 and the second one is of type variadic<Address>. All the arguments in this file should be placed inside a list. The arguments file should look like this:

[
2,
[
{
"bech32": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"
},
{
"hex": "8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8"
}
]
]

Let's go a bit through our file and see why it looks like this. First, as mentioned above, we have to place all the arguments inside a list. Then, the value 2 corresponds to the type u32. After that, we have another list that coresponds to the type variadic. Inside this list, we need to insert our addresses. For mxpyto encode addresses properly, we need to provide the address values inside a dictionary that can contain two keys: we can provide the address as the bech32 representation or as the hex encoded public key.

After finishing the arguments file, we can run the following command to deploy the contract:

mxpy contract deploy --bytecode ~/contracts/multisig/output/multisig.wasm \
--proxy=https://devnet-gateway.multiversx.com --recall-nonce \
--abi ~/contracts/multisig/output/multisig.abi.json \
--arguments-file deploy_multisig_arguments.json \
--gas-limit 500000000 \
--pem=~/multiversx-sdk/testwallets/latest/users/alice.pem \
--send

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.

Calling the smart contract providing the ABI file

Same as we did for deploying the contract, we can call functions by providing the ABI file and the arguments file.

Since we deployed the multisig contract, we'll call the proposeTransferExecute endpoint.

First, we'll prepare the file containing the endpoints arguments. We'll refer to this file as call_multisig_arguments.json. The proposeTransferExecute endpoint requires four arguments, the first is of type Address, the second one is of type BigUInt, the third is of type Option<u64> and the fourth is of type variadic<bytes>. All the arguments in this file should be placed inside a list. The arguments file should look like this:

[
{
"bech32": "erd1qqqqqqqqqqqqqpgqs63rcpahnwtjnedj5y6uuqh096nzf75gczpsc4fgtu"
},
1000000000000000000,
5000000,
[
{
"hex": "616464403037"
}
]
]

Let's go a bit through our file and see why it looks like this. First, as mentioned above, we have to place all the arguments inside a list. Then, the contract expects an address, so we provide the bech32 representation. After that, we have a BigUInt value that we can provide as a number. The third value is Option<u64>, so we provide it as a number, as well. In case we wanted to skip this value, we could've simply used 0. The last parameter is of type variadic<bytes>. Because it's a variadic value, we have to place the arguments inside a list. Since we can't write bytes, we hex encode the value and place it in a dictionary containing the key-value pair "hex": "<hex_string>", same as we did above for the address.

After finishing the arguments file, we can run the following command to call the endpoint:

mxpy contract call erd1qqqqqqqqqqqqqpgqjsg84gq5e79rrc2rm5ervval3jrrfvvfd8sswc6xjy \
--proxy=https://devnet-gateway.multiversx.com --recall-nonce \
--abi ~/contracts/multisig/output/multisig.abi.json \
--arguments-file call_multisig_arguments.json \
--function proposeTransferExecute
--gas-limit 500000000 \
--pem=~/multiversx-sdk/testwallets/latest/users/alice.pem \
--send

Querying the 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.

Querying the smart contract providing the ABI file

We'll call the signed readonly endpoint of the multisig contract. This endpoint accepts two arguments: the first is the address, and the second is the proposal ID, which will be used to verify if the address has signed the proposal. The endpoint returns a boolean value, true if the address has signed the proposal and false otherwise.

Let's prepare the arguments file. The first argument is of type Address and the second one is of type u32, so our file looks like this:

[
{
"bech32": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"
},
1
]

As above, we encapsulate the address in a dictionary and the u32 value is simply a number. We'll refer to this file as query_multisig_arguments.json.

After preparing the file, we can run the following command:

mxpy contract query erd1qqqqqqqqqqqqqpgqjsg84gq5e79rrc2rm5ervval3jrrfvvfd8sswc6xjy \
--proxy https://devnet-gateway.multiversx.com \
--function signed \
--abi ~/contracts/multisig/output/multisig.abi.json \
--arguments-file query_multisig_arguments.json

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.

As shown above, we can also upgrade the contract by providing the ABI file and the arguments file:

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

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".

note
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.

Relayed V3 transactions

Relayed transactions are transactions with the fee paid by a so-called relayer. In other words, if a relayer is willing to pay for a transaction, it is not mandatory for the sender to have any EGLD for fees. To learn more about relayed transactions check out this page.

In this section we'll see how we can send Relayed V3 transactions using mxpy. For a more detailed look on Relayed V3 transactions, take a look here. For these kind of transactions a new transaction field has been introduced, called innerTransactions. In this example we'll see how we can create both the inner transactions and the relayed transaction.

Creating the inner transactions

We can simply create the inner transactions the same way we did above, by using the mxpy tx new command. The only difference is that we'll have to provide an additional argument called --inner-transactions-outfile, which represents the file where the inner transactions are saved to be later used by the relayer. To keep it simple, we'll send 1 EGLD from Alice to Bob, and Carol will be the relayer. To create the EGLD transfer transaction from Alice to Bob, we run the following command:

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 \
--relayer erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8
--inner-transactions-outfile inner_transactions.json

After creating the inner transaction, we are ready to create the relayed transaction. We have to keep in mind that for Relayed V3 transactions, the receiver has to be the same as the relayer, in our case, Carol. Another requirement is that the relayed transaction has to have enough gas. The gas is computed by multiplying the base cost (50_000) with the number of inner transactions plus the gasLimit for each inner transaction. For more details on how the gas is computed, check out this page.

We can create the relayed transaction by running the following command:

mxpy tx new --pem ~/multiversx-sdk/testwallets/latest/users/carol.pem --recall-nonce \
--receiver erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8 \
--gas-limit 1000000 --value 0 \
--proxy https://devnet-gateway.multiversx.com --chain D \
--inner-transactions inner_transactions.json \
--send

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.