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.
Generally speaking, the default rust
version installed by mxpy
is the one referenced by the latest Docker image used for reproducible builds.
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.
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 textkeystore-mnemonic
- secret phrase, as a password-encrypted JSON keystore filekeystore-secret-key
- secret key (irreversibly derived from the secret phrase), as a password-encrypted JSON keystore filepem
- 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
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 formatkeystore-mnemonic
can be converted to any other formatkeystore-secret-key
can only be converted topem
pem
can only be converted tokeystore-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 mxpy
to 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
The account that triggers the code verification process must be the owner of the contract.
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".
Here are the urls to our hosted co-signer services:
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
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
As of October 2023, on Windows (or WSL), you might encounter some issues when trying to use Ledger in mxpy
.