Tutorial: Querying a MultiversX Smart Contract using mxpy

Quentin Diebold
8 min readApr 18, 2024

--

Even if you’re not a developer, knowing how to query a smart contract enables you to retrieve crucial information without relying on any dApp 🔥

Let me show you step by step how to do this!

Here’s the plan for this article:

1️) Prerequisites and introduction to mxpy

2) How to know which information you can retrieve from a contract

3) Querying a smart contract: general basics

4️) Concrete example with xExchange: a token sniping bot

This article is the first one of a new series: interacting with the #MultiversX blockchain 👀

Prerequisites and introduction to mxpy

mxpy is a CLI, Command Line Interface, that allows you to do several things on the MultiversX blockchain. Since it is a CLI, you will have to use it through your computer’s terminal, here are the prerequisites:

  • Having an Ubuntu/macOS terminal: if you’re on Windows, don’t worry — you can install WSL with just one click, which provides you with an Ubuntu terminal. I’ll provide you a link to install it in the comments. From this point you’ll have to do all the operations in this article inside WSL
  • Knowing how to launch a command in the terminal: no worries you don’t need to know more
  • If you’re on macOS you should have homebrew installed. To do so you can run the following command: /bin/bash -c “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

It’s time to install mxpy! The first step is to install pipx, a tool that makes installing Python CLIs easy

For macOS

macOS users have to run the following commands:

brew install pipx
pipx ensurepath
sudo pipx ensurepath --global

For Ubuntu or WSL users

Ubuntu or WSL users have to run the following commands:

sudo apt update
sudo apt install pipx
pipx ensurepath
sudo pipx ensurepath --global

You may need to restart your terminal. Once you have installed pipx, you can install mxpy with a single command:

pipx install multiversx-sdk-cli --force

Restart your terminal and run the following command to check if mxpy has been correctly installed:

mxpy --version

If you get a response like this: MultiversX Python CLI (mxpy) <version number> then you have installed mxpy successfully and you can go to the next steps!

How to know which information you can retrieve from a contract

Before querying a smart contract, you need to know its address, which you can determine in one of five ways:

  • Delving into the explorer to see useful transactions that lead to the contracts you aim for
  • Knowing the admin of the contracts, then you can check on the explorer the contracts it has deployed
  • Going to the dApp and inspecting the developer console, especially the “Network” tab, to try to retrieve a list of interesting addresses
  • Retrieving a transaction you did through the dApp, the receiver have great chances to be a useful contract
  • Asking someone who knows: the project that owns the contract, a project that uses the contracts, @Foudres because he knows everything happening on the blockchain, etc…

Example with xExchange

You want to get information about the xExchange liquidity pair USDC/WEGLD. This pool is frequently used, making it a good idea to initially delve into the explorer. Once you’re on the explorer you can go to the “Tokens” page to click on the USDC token, you’ll see all the transactions involving USDC. You just have to find a transaction that performs a swap and you’ll see the liquidity pool contract address: erd1qqqqqqqqqqqqqpgqeel2kumf0r8ffyhth7pqdujjat9nx0862jpsg2pqaq

You have the address, it’s time to know the information you can retrieve from it! A smart contract is a set of functions one can call. We’re looking for a specific type of functions called “views”, the sole purpose of a view is to retrieve a specific information about the contract. In our example, any xExchange liquidity pool contract contains a lot of view such as:

  • ‘getFirstTokenId’, which gives you… the first token of the pair
  • ‘getAmountOut’, which estimates the amount of a swap without the need to perform it
  • ‘getTotalFeePercent’, which gives you the % of the fees for each swap
  • etc…

Your goal is to find the views that provide the information you need. Depending on the context, you will choose one of the following methods:

  • You have access to the ABI. This is the ideal scenario because the ABI is a JSON file referencing all the views! A project can give you the ABI of its contract
  • The contract’s code is open source. You can either directly read the code and search for “#[view(” to see the views or if you’re a developer you can compile yourself the ABI
  • Reading directly from the raw WASM of the contract. If you’re totally desperate you can download the wasm code of the contract from the explorer, convert it to wat through an online tool such as wasm2wat so you can read it, and find for “export” functions. This is hard and you probably don’t want to do this

You have the contract address and the views that interests you, let’s use mxpy to query them!

Querying a smart contract: general basics

Let’s see the general way to perform a query using mxpy. It will be a bit abstract and you probably won’t understand everything at first, but once I’ll show you some concrete examples everything will become super clear!

mxpy has a command dedicated to query a contract’s views: mxpy contract query. Let’s see how to use it

The general form of the command is as follows:

mxpy contract query <contract’s address> \
--proxy <proxy url> \
--function <view name> \
--arguments <parameters of the view if any, separated by a space>

Here are more details about each parameter:

  • contract’s address: the erd… address of the contract such as erd1qqqqqqqqqqqqqpgqeel2kumf0r8ffyhth7pqdujjat9nx0862jpsg2pqaq
  • proxy: the proxy used to communicate with the blockchain, if you don’t know what it is you can provide the mainnet official one: https://gateway.multiversx.com
  • arguments: some views can ask arguments, you have to provide them here. For example the “getAmountOut” view of a xExchange pair, which simulates the result of a swap, asks for 2 arguments: the input token and the input amount of the swap. For views that doesn’t require any argument, you should not pass the — arguments in the mxpy command

About the argument format, it depends of what the view asks for, but here’s the general method:

  • For a number : “<the number>”, for example “1600”. Be careful because 1 EGLD is 10¹⁸, so “1000000000000000000”
  • For a text or a token identifier: “str:<the text>”, for examples “str:Hello World” or “str:USDC-c76f1”
  • For an address: “<the address>”, for example “erd1qqqqqqqqqqqqqpgqeel2kumf0r8ffyhth7pqdujjat9nx0862jpsg2pqaq”
  • For arbitrary hexadecimal data: “0x<the hex bytes>”, for example “0x1af76d”. However you probably won’t use this because it’s not human readable

The arguments should always be enclosed in quotes and separated by spaces. The following is valid for a view asking for a token identifier, an address, and an amount:

mxpy [...] --arguments “str:USDC-c76f1” “erd1qqqqqqqqqqqqqpgqeel2kumf0r8ffyhth7pqdujjat9nx0862jpsg2pqaq” “1000000000000000000”

When you run the command in the terminal, you will get the output in different encodings: base64, hex, decimal. If the expected result of the view is a number, look at the decimal value. If you expect a text or an erd address, such as the name of a token, you have to take the hex value and and decode it using the following tool: https://utils.multiversx.com/converters

If you expect a complex output, then you should use a more elaborated tool than mxpy. This is the topic of another article

I know it sounds a bit abstract, that’s why I’ll show you concrete examples with the xExchange!

Concrete example with xExchange: a token sniping bot

Suppose you want to create a bot that snipes a specific token once the swap are enabled. You’ll need to automatically query the following information:

  1. The token xExchange pair contract address
  2. The state of the pair, to know whether the swaps are enabled
  3. The liquidity, so you don’t snipe a token which has almost no liquidity. It would result in a huge price impact
  4. The estimated amount out, so you can apply a slippage

Let’s take a concrete token live on the mainnet: BOBER-9eb764. The first thing we want to do is to retrieve the WEGLD-bd4d79/BOBER-9eb764 pair contract address. To do so, the xExchange’s router contract is our best ally, this contract has a view called ‘getPair’ that gives us the contract address of a pair, if it exists. The router address is erd1qqqqqqqqqqqqqpgqq66xk9gfr4esuhem3jru86wg5hvp33a62jps2fy57p and the view asks for two arguments: first_token_id and second_token_id, both are token identifiers

Here’s the command to query the getPair view on the xExchange router contract:

mxpy contract query erd1qqqqqqqqqqqqqpgqq66xk9gfr4esuhem3jru86wg5hvp33a62jps2fy57p \
--proxy https://gateway.multiversx.com \
--function “getPair” \
--arguments “str:WEGLD-bd4d79” “str:BOBER-9eb764”

The hexadecimal output: 00000000000000000500490c9a6ff1c993e899c9d9df08eecca3f748bd295483

Let’s use the tool I mentioned above to convert it to an address: erd1qqqqqqqqqqqqqpgqfyxf5ml3exf73xwfm80s3mkv50m530ff2jpsznpumc

Note: If the pair doesn’t exist, the view will output the zero address: 0000000000000000000000000000000000000000000000000000000000000000 (in hex) = erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu

Once you have identified the pair, you need to check whether the swaps are enabled. You can call the “getState” view on the pair, this view don’t have any argument and returns the hex value 00, 01 or 02 for respectively “Inactive”, “Active” and “PartiallyActive”. What interest us is the “Active” state, let’s query it:

mxpy contract query erd1qqqqqqqqqqqqqpgqfyxf5ml3exf73xwfm80s3mkv50m530ff2jpsznpumc \
--proxy https://gateway.multiversx.com \
--function “getState”

The hexadecimal output is: 01, bingo swaps are enabled!

At this point, we already can send a transaction to swap our WEGLD to get some BOBER, but let’s be precautious, maybe there is 10$ worth of liquidity in the pair, resulting in a huge loss due to the price impact. To counter this, we have to query the liquidity of the pair

For simplicity we’ll only query the liquidity of the WEGLD token. The pair contract has a view called ‘getReserve,’ which requires a single token identifier as an argument and returns the liquidity for that token. Let’s do the query:

mxpy contract query erd1qqqqqqqqqqqqqpgqfyxf5ml3exf73xwfm80s3mkv50m530ff2jpsznpumc \
--proxy https://gateway.multiversx.com \
--function “getReserve" \
--arguments “str:WEGLD-bd4d79”

Since the view returns a number, we can directly read the decimal return: 5096579809102999849080. Please remember that 1 WEGLD = 10¹⁸, therefore 5096579809102999849080 = 5096.579809102999849080 EGLD

If you find the liquidity sufficient, you can go to the last step: simulating the output of the swap. In order to compute the result of a swap, one has to query the “getAmountOut” view present on the pair contract. This view takes two arguments: token_in which is a token identifier and amount_in which is a number

token_in is the input token: WEGLD-bd4d79. Let’s suppose you want to swap 10 WEGLD, the amount you have to input as argument is “10000000000000000000”

Here is the query:

mxpy contract query erd1qqqqqqqqqqqqqpgqfyxf5ml3exf73xwfm80s3mkv50m530ff2jpsznpumc \
--proxy https://gateway.multiversx.com \
--function “getAmountOut” \
--arguments “str:WEGLD-bd4d79” “10000000000000000000”

This view also returns a number; we can directly interpret the decimal result: 737369342289264796533510 = 737369.342289264796533510 BOBER

You have everything needed to take your decision and apply a slippage if you want to! The very next step is to send a real transaction to perform the swap! 🔥 It can also be done through mxpy but this the subject of another article

I hope you enjoyed this article! If so, please share it and give me a follow ❤️

--

--