From e3dafe32a00f5b66941c366f3474e3645a7e8fb0 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 14 Oct 2025 15:50:11 +0300 Subject: [PATCH 01/15] sdk-rs - toolchain setup page --- ...t-nightly.md => rust-stable-vs-nightly.md} | 22 ++-- docs/developers/toolchain-setup.md | 99 +++++++++++++++ .../tutorials/chain-simulator-adder.md | 41 +++++-- docs/developers/tutorials/crowdfunding-p1.md | 4 +- .../developers/tutorials/interactors-guide.md | 17 +-- docs/developers/tutorials/staking-contract.md | 4 +- docs/developers/tutorials/your-first-dapp.md | 4 +- .../mxpy/smart-contract-interactions.md | 4 +- .../troubleshooting/ide-setup.md | 2 +- .../troubleshooting/rust-setup.md | 115 +----------------- .../troubleshooting/troubleshooting.md | 2 +- sidebars.js | 5 +- 12 files changed, 171 insertions(+), 148 deletions(-) rename docs/developers/meta/{rust-nightly.md => rust-stable-vs-nightly.md} (69%) create mode 100644 docs/developers/toolchain-setup.md diff --git a/docs/developers/meta/rust-nightly.md b/docs/developers/meta/rust-stable-vs-nightly.md similarity index 69% rename from docs/developers/meta/rust-nightly.md rename to docs/developers/meta/rust-stable-vs-nightly.md index 913943227..facd647be 100644 --- a/docs/developers/meta/rust-nightly.md +++ b/docs/developers/meta/rust-stable-vs-nightly.md @@ -1,5 +1,5 @@ --- -id: rust-nightly +id: rust-stable-vs-nightly title: Stable vs. Nightly Rust --- @@ -17,22 +17,28 @@ Before this version, nightly Rust was required. For everything after v0.50.0 we recommend running the latest stable version of Rust. Older versions have had compatibility issues with certain framework dependencies, on certain versions of the compiler. -Nightly Rust is still allowed, but not recommended. We will still be supporting nightly builds and running continuous integration on `nightly-2024-05-22`. - Also, everything on versions older than v0.50.0 needs to run on nightly Rust. -So, to summarize: -- Before `v0.50`: `nightly-2023-12-11` and `nightly-2024-05-22` are both known to be running fine; -- On `v0.50` (use `v0.50.6`): any version of `stable` Rust; -- Starting with `v0.51`: `stable`, Rust >= 1.78 required. +:::important + +- For Versions Prior to `v0.50`: + - **Requirement**: A specific nightly build is necessary. + - **Known Working Builds**:`nightly-2023-12-11` and `nightly-2024-05-22` are confirmed to run correctly. +- For Versions `v0.50` through `v0.57`: + - **Requirement**: The **stable channel** must be used, requiring a Rust version that is **greater than or equal** to `1.78` and **less than or equal** to `1.86`. +- For Version `v0.58` and Higher: + - **Requirement**: The **stable channel** must be used, requiring a **minimum** Rust version of `1.78` or newer. + +::: [comment]: # (mx-context-auto) -## Why Nighly for the older versions? +## Why Nightly for the older versions? There were several nightly features that the framework was using, which we had hoped to see stabilized sooner. These are of little relevance to the average developer, but for the record, let's mention a few of them and how we managed to circumvent their usage: + - `never_type` - avoided by using slightly different syntax; - `auto_traits` and `negative_impls` - avoided by redesigning the `CodecFrom`/`TypeAbiFrom` trait systems; - `generic_const_exprs` - replaced with massive amounts of macros; diff --git a/docs/developers/toolchain-setup.md b/docs/developers/toolchain-setup.md new file mode 100644 index 000000000..dee54d32e --- /dev/null +++ b/docs/developers/toolchain-setup.md @@ -0,0 +1,99 @@ +--- +id: toolchain-setup +title: Toolchain Setup +--- + +[comment]: # (mx-context-auto) + +## Installing Rust and sc-meta + +:::note +`sc-meta` is universal smart contract management tool. Please follow [this](/developers/meta/sc-meta) for more information. +::: + +[comment]: # (mx-context-auto) + +For systems running Ubuntu or Windows with WSL, you should first ensure the following system-level dependencies required by Rust and sc-meta are in place: + +```bash +sudo apt-get install build-essential pkg-config libssl-dev +``` + +Install Rust as recommended on [rust-lang.org](https://www.rust-lang.org/tools/install): + +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +Then, choose **Proceed with installation (default)**. + +:::tip +Generally speaking, you should install Rust `v1.85.0` (stable channel) or later. +::: + +```bash +rustup update +rustup default stable +``` + +Afterwards, open a new terminal (shell) and install `sc-meta`: + +```bash +cargo install multiversx-sc-meta --locked +``` + +Once `sc-meta` is ready, install the `wasm32` target (for the Rust compiler), `wasm-opt`, and others dependencies as follows: + +```bash +# Installs `wasm32`, `wasm-opt`, and others in one go: +sc-meta install all + +cargo install twiggy +``` + +[comment]: # (mx-context-auto) + +### Within Continuous Integration / Continuous Delivery + +For automated environments like CI/CD pipelines, start by installing the necessary foundational libraries. On platforms such as Ubuntu (or WSL), this means installing: + +```bash +sudo apt-get install build-essential pkg-config libssl-dev +``` + +For CI / CD, install Rust as follows: + +```bash +wget -O rustup.sh https://sh.rustup.rs && \ + chmod +x rustup.sh && \ + ./rustup.sh --verbose --default-toolchain stable -y + +cargo install multiversx-sc-meta --locked + +sc-meta install wasm32 +``` + +[comment]: # (mx-context-auto) + +## Check your Rust installation + +You can check your Rust installation by invoking `rustup show`: + +```sh +$ rustup show + +Default host: x86_64-unknown-linux-gnu +rustup home: /home/ubuntu/.rustup + +installed toolchains +-------------------- +stable-x86_64-unknown-linux-gnu (default) +[...] + +active toolchain +---------------- +name: stable-x86_64-unknown-linux-gnu +installed targets: + wasm32-unknown-unknown + wasm32v1-none + x86_64-unknown-linux-gnu diff --git a/docs/developers/tutorials/chain-simulator-adder.md b/docs/developers/tutorials/chain-simulator-adder.md index f09a156cb..f1a4ad532 100644 --- a/docs/developers/tutorials/chain-simulator-adder.md +++ b/docs/developers/tutorials/chain-simulator-adder.md @@ -5,20 +5,22 @@ title: Chain Simulator in Adder - SpaceCraft interactors [comment]: # (mx-abstract) -This tutorial will guide you to interact with **Chain Simulator** using the SpaceCraft interactors in _Adder_ smart contract. +This tutorial will guide you to interact with **Chain Simulator** using the SpaceCraft interactors in _Adder_ smart contract. [comment]: # (mx-context-auto) ## Introduction -[Chain Simulator](../../sdk-and-tools/chain-simulator.md) mimics the functionality of a local blockchain test network. It offers a convenient, faster, and realistic way to test smart contracts. +[Chain Simulator](../../sdk-and-tools/chain-simulator.md) mimics the functionality of a local blockchain test network. It offers a convenient, faster, and realistic way to test smart contracts. [SpaceCraft interactor](../meta/interactor/interactors-overview.md) allows testing any complex scenario defined in a smart contract using **Chain Simulator**. Rather than going through the full setup of a local testnet, you can get straight to developing and debugging in a streamlined environment. This tool handles the setup details so you can focus on what matters most: **building and testing your contracts**. :::important Before we dive in and explore how easy it can be, make sure you have the following: -- `stable` Rust version `≥ 1.78.0` (install via [rustup](https://docs.multiversx.com/sdk-and-tools/troubleshooting/rust-setup/#without-mxpy)): -- `multiversx-sc-meta` version `≥ 0.54.0` (cargo install [multiversx-sc-meta](https://docs.multiversx.com/developers/meta/sc-meta-cli/#introduction)) + +- `stable` Rust version `≥ 1.85.0` (install via [rustup](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta)): +- `multiversx-sc-meta` version `≥ 0.54.0` (cargo install [multiversx-sc-meta](/docs/developers/meta/sc-meta-cli.md)) + ::: [comment]: # (mx-context-auto) @@ -54,7 +56,7 @@ After running the command, you’ll see that the contract `my-adder` has been ge Our main focus will be the `interactor` directory. Let’s take a closer look: -The directory that makes the connection with Chain Simulator contains the following structures: +The directory that makes the connection with Chain Simulator contains the following structures: ```bash . @@ -84,10 +86,11 @@ gateway_uri = 'http://localhost:8085' ``` You can customize two settings in the configuration: + - **Type of blockchain**; - **Gateway URI**. -To use a simulator for your blockchain, set `chain_type` as shown in the example above. +To use a simulator for your blockchain, set `chain_type` as shown in the example above. By default, the simulator runs on `http://localhost:8085`. However, depending on your Docker image settings, the simulator's URI **might have a different port or name**. @@ -96,6 +99,7 @@ Make sure to set both `chain_type` and `gateway_uri` for the interactor to work. ::: The configuration is parsed by `basic_interactor_config.rs`. This file contains **two** functions that will be important for interacting with Chain Simulator: + - `use_chain_simulator()`: returns if the chain type is real or simulator; - `chain_simulator_config()`: initialize the proper configuration for simulator environment; this function is useful for **continuous integration tests**. @@ -172,11 +176,12 @@ pub async fn new(config: Config) -> Self { } ``` -Let's find out what is mandatory for initializing the Chain Simulator interactor. +Let's find out what is mandatory for initializing the Chain Simulator interactor. ```rust let adder_owner_address = interactor.register_wallet(test_wallets::heidi()).await; ``` + Every time you initialize an interactor, you’ll need to register a wallet. When a wallet is registered in the Chain Simulator, its associated account is automatically credited with a generous amount of EGLD. This way, you don’t have to worry about running out of tokens while testing! :::tip @@ -190,13 +195,14 @@ Whenever the Chain Simulator stops, the account will be dissolved. ```rust interactor.generate_blocks_until_epoch(1).await.unwrap(); ``` + Node enables `ESDTSystemSCAddress` in **epoch number one**. If you want to use functionality like issuing or minting tokens, it is necessary to generate blocks until the simulator chain reaches **epoch number one**. [comment]: # (mx-context-auto) ## Step 4: Create tests that run on Chain Simulator -One of the best parts about using the Chain Simulator with your interactor is that it lets you create **continuous integration tests in an environment that mirrors the real blockchain**. +One of the best parts about using the Chain Simulator with your interactor is that it lets you create **continuous integration tests in an environment that mirrors the real blockchain**. `tests/` holds all your test suites, where you are able to verify your contract’s behaviour effectively. @@ -277,6 +283,7 @@ async fn simulator_adder_test() {} ### 1. Deploy the contract **Deploy** on Chain Simulator _MyAdder_ contract which sets the initial sum with **zero**. + ```rust use basic_interactor::{Config, MyAdderInteract}; @@ -369,6 +376,7 @@ async fn simulator_upgrade_test() { [comment]: # (mx-context-auto) ### 5. Unauthorized Upgrade + Attempt to upgrade the contract with an unauthorized user to confirm that it results in a failed transaction. To ensure no changes occur, you need to query the storage again to check that the number remains unchanged. ```rust @@ -417,6 +425,7 @@ async fn simulator_upgrade_test() { [comment]: # (mx-context-auto) ### Install + To run the test provided, you will need to install the Docker image that includes the Chain Simulator. ```bash @@ -427,21 +436,26 @@ Successfully pulled the latest Chain Simulator image. :::note If you encounter the following error while installing: + ```bash Attempting to install prerequisites for the Chain Simulator... Error: Failed to execute command: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock ``` You will need to run the command with root privileges due to Docker usage: + ```bash my-adder/interactor$ sudo sc-meta cs install ``` If you get this error: + ```bash sudo: sc-meta: command not found ``` + You can find the sc-meta path and choose one of these solutions: + ```bash my-adder/interactor$ which sc-meta my-path/.cargo/bin/sc-meta @@ -449,9 +463,11 @@ my-path/.cargo/bin/sc-meta 1. Add the sc-meta path to root privileges: 2. Run sc-meta directly using the full path: + ```bash sudo my-path/.cargo/bin/sc-meta cs install ``` + ::: [comment]: # (mx-context-auto) @@ -459,6 +475,7 @@ sudo my-path/.cargo/bin/sc-meta cs install ### Start Once you’ve successfully installed the Docker image, you can start the Chain Simulator. + ```bash my-adder/interactor$ sudo my-path/.cargo/bin/sc-meta cs start Attempting to start the Chain Simulator... @@ -477,6 +494,7 @@ INFO [2024-11-11 13:09:15.699] updated config value file = [comment]: # (mx-context-auto) ### Run + While Chain Simulator is running, open a new terminal window in parallel. In this new terminal, you will run the test provided in [Step 4](./chain-simulator-adder.md#step-4-create-tests-that-run-on-chain-simulator). ```bash @@ -489,17 +507,22 @@ test simulator_upgrade_test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.09s ``` + [comment]: # (mx-context-auto) ### Stop + In the **same** terminal window you ran the tests, **stop** Chain Simulator using the next command: + ```bash my-adder/interactor$ sc-meta cs stop Attempting to close the Chain Simulator... Successfully stopped the Chain Simulator. ``` + :::note If you encounter the following error while stopping: + ```bash Attempting to close the Chain Simulator... Error: Failed to execute command: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock @@ -508,4 +531,4 @@ Error: Failed to execute command: permission denied while trying to connect to t Solution is presented in [Run](./chain-simulator-adder.md#install) section. ::: -By following these steps, you've mastered the basics of smart contract development and testing. Now, it's time to explore more advanced techniques and create innovative applications :sparkles: :rocket: \ No newline at end of file +By following these steps, you've mastered the basics of smart contract development and testing. Now, it's time to explore more advanced techniques and create innovative applications :sparkles: :rocket: diff --git a/docs/developers/tutorials/crowdfunding-p1.md b/docs/developers/tutorials/crowdfunding-p1.md index 2052114be..d3f83f53f 100644 --- a/docs/developers/tutorials/crowdfunding-p1.md +++ b/docs/developers/tutorials/crowdfunding-p1.md @@ -52,8 +52,8 @@ Automated testing is exceptionally important for the development of smart contra :::important Before starting this tutorial, make sure you have the following: -- `stable` **Rust** version `≥ 1.83.0` (install via [rustup](/docs/sdk-and-tools/troubleshooting/rust-setup.md#installing-rust-and-sc-meta)) -- `sc-meta` (install [multiversx-sc-meta](/docs/sdk-and-tools/troubleshooting/rust-setup.md#installing-rust-and-sc-meta)) +- `stable` **Rust** version `≥ 1.85.0` (install via [rustup](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta)) +- `sc-meta` (install [multiversx-sc-meta](/docs/developers/meta/sc-meta-cli.md)) ::: diff --git a/docs/developers/tutorials/interactors-guide.md b/docs/developers/tutorials/interactors-guide.md index a229c7392..17011b49e 100644 --- a/docs/developers/tutorials/interactors-guide.md +++ b/docs/developers/tutorials/interactors-guide.md @@ -21,8 +21,9 @@ In order to make sure that the smart contract works as expected, there are at le In this tutorial we will focus on integration testing using the interactors made available by the SpaceCraft smart contract framework. ::::important Prerequisites -- `stable` Rust version `1.78.0 or above` (install via [rustup](https://docs.multiversx.com/sdk-and-tools/troubleshooting/rust-setup/#without-mxpy)): -- `multiversx-sc-meta` version `0.50.0 or above` (cargo install [multiversx-sc-meta](https://docs.multiversx.com/developers/meta/sc-meta-cli/#introduction)) + +- `stable` Rust version `1.78.0 or above` (install via [rustup](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta)): +- `multiversx-sc-meta` version `0.50.0 or above` (cargo install [multiversx-sc-meta](/docs/developers/meta/sc-meta-cli.md)) :::: [comment]: # (mx-context-auto) @@ -110,7 +111,7 @@ sc-meta all snippets This command compiled the contract and generated a new folder called `interactor`. The interactor is by default a Rust CLI program that uses the smart contract proxy to send calls to the contract. -Inside the source folder *(interactor/src)*, we should find the newly generated proxy of the contract *(proxy.rs)* and the `interactor_main.rs` file, which is the main file of the project. A *sc-config.toml* file has also been created (if not existent) containing the path of the proxy file. +Inside the source folder *(interactor/src)*, we should find the newly generated proxy of the contract *(proxy.rs)* and the `interactor_main.rs` file, which is the main file of the project. A *sc-config.toml* file has also been created (if not existent) containing the path of the proxy file. If we navigate to *interactor/src/interactor_main.rs*, inside the `main` function, we can find all the CLI command available to us: @@ -170,7 +171,7 @@ By default, the testing environment is `devnet`, specified by the `GATEWAY` cons ```rust title=interactor_main.rs const GATEWAY: &str = sdk::blockchain::DEVNET_GATEWAY; ``` -Changing the value of this constant will change the testing environment for a quick setup (other options are `TESTNET_GATEWAY` and `MAINNET_GATEWAY`). +Changing the value of this constant will change the testing environment for a quick setup (other options are `TESTNET_GATEWAY` and `MAINNET_GATEWAY`). Each command has some waiting time and returns the result inside a variable in the function, but also prints it in the console for easy tracking. @@ -267,11 +268,11 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini This setup can be used for extensive testing, but also as a tool for live deployment on mainnet, tracking and interaction. In a multi-contract setup, one can, for example, create different modules for specific interactions with each contract and development environment and further structure all the interactions into different integration tests. -Let’s take the example of the [DEX smart contract interactors](https://github.com/multiversx/mx-exchange-sc/tree/feat/unified/dex/interactor). Here, all the proxy files are organized in a different crate for easier access. +Let’s take the example of the [DEX smart contract interactors](https://github.com/multiversx/mx-exchange-sc/tree/feat/unified/dex/interactor). Here, all the proxy files are organized in a different crate for easier access. ![img](/img/dex_interactor_file_structure.jpeg) -Furthermore, all the contracts that are part of the DEX flow have separate interaction modules, so we can easily keep track of the flow when writing complex tests. +Furthermore, all the contracts that are part of the DEX flow have separate interaction modules, so we can easily keep track of the flow when writing complex tests. This is the `energy_factory` file, containing only interactions with the `energy factory smart contract`, using the specific proxy and contract address: ```rust title=energy_factory.rs @@ -302,7 +303,7 @@ pub(crate) async fn get_energy_amount_for_user( } ``` -After having implemented this structure, writing integration test is a smooth process, even though the logic gets complicated: +After having implemented this structure, writing integration test is a smooth process, even though the logic gets complicated: ```rust title=dex_interact.rs impl DexInteract { @@ -354,7 +355,7 @@ pub mod integration_tests { } ``` -Organizing the code this way streamlines the process even further. Now, it is just a matter of using a different datatype or a different module in order to keep track of the various contracts and development environments and be able to rerun everything quickly if needed. +Organizing the code this way streamlines the process even further. Now, it is just a matter of using a different datatype or a different module in order to keep track of the various contracts and development environments and be able to rerun everything quickly if needed. [comment]: # (mx-context-auto) diff --git a/docs/developers/tutorials/staking-contract.md b/docs/developers/tutorials/staking-contract.md index 813449f63..262f5694d 100644 --- a/docs/developers/tutorials/staking-contract.md +++ b/docs/developers/tutorials/staking-contract.md @@ -20,8 +20,8 @@ If you find anything not answered here, feel free to ask further questions on th :::important Before starting this tutorial, make sure you have the following: -- `stable` **Rust** version `≥ 1.83.0` (install via [rustup](/docs/sdk-and-tools/troubleshooting/rust-setup.md#installing-rust-and-sc-meta)) -- `sc-meta` (install [multiversx-sc-meta](/docs/sdk-and-tools/troubleshooting/rust-setup.md#installing-rust-and-sc-meta)) +- `stable` **Rust** version `≥ 1.85.0` (install via [rustup](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta)) +- `sc-meta` (install [multiversx-sc-meta](/docs/developers/meta/sc-meta-cli.md)) ::: diff --git a/docs/developers/tutorials/your-first-dapp.md b/docs/developers/tutorials/your-first-dapp.md index e0734bbdb..169e1587f 100644 --- a/docs/developers/tutorials/your-first-dapp.md +++ b/docs/developers/tutorials/your-first-dapp.md @@ -12,8 +12,8 @@ Let's build your first decentralized application(dApp) on the MultiversX Blockch :::important Before starting this tutorial, make sure you have the following: -- `stable` Rust version `≥ 1.78.0` (install via [rustup](https://docs.multiversx.com/sdk-and-tools/troubleshooting/rust-setup/#without-mxpy)) -- `multiversx-sc-meta` (cargo install [multiversx-sc-meta](https://docs.multiversx.com/developers/meta/sc-meta-cli/#introduction)) +- `stable` Rust version `≥ 1.85.0` (install via [rustup](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta)) +- `multiversx-sc-meta` (cargo install [multiversx-sc-meta](/docs/developers/meta/sc-meta-cli.md)) - `Node.js` with version `≥ 20`(guide [here](https://nodejs.org/en/download/package-manager)) - `yarn` ([npm install --global yarn](https://classic.yarnpkg.com/lang/en/docs/install/#debian-stable) ) diff --git a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md index 4e1f4e0b4..72caa0589 100644 --- a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md +++ b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md @@ -34,8 +34,7 @@ We're going to use [**mxpy**](/sdk-and-tools/mxpy/mxpy-cli) to deploy the contra ### Rust -Install **Rust** and [**sc-meta**](/developers/meta/sc-meta) as depicted [here](/sdk-and-tools/troubleshooting/rust-setup). They are required to build smart contracts. - +Install **Rust** and [**sc-meta**](/developers/meta/sc-meta) as depicted [here](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta). They are required to build smart contracts. ## Deploy & Upgrade @@ -139,6 +138,7 @@ mxpy facilitates us with some encoding conventions, including: - The values **true** or **false** are automatically converted to **boolean** values - Values that are identified as **numbers** are hex encoded by default - Arguments like **0x...** are left unchanged, as they are interpreted as already encoded hex values + ::: So, in case of our **myNonPayableEndpoint** interaction, we can write it like so: diff --git a/docs/sdk-and-tools/troubleshooting/ide-setup.md b/docs/sdk-and-tools/troubleshooting/ide-setup.md index b2c79d95b..21a317793 100644 --- a/docs/sdk-and-tools/troubleshooting/ide-setup.md +++ b/docs/sdk-and-tools/troubleshooting/ide-setup.md @@ -19,6 +19,6 @@ If `rust-analyzer` is not working properly on VSCode, you might see (one of) the - error: rustup could not choose a version of cargo to run, because one wasn't specified explicitly, and no default is configured. ``` -If so, **[make sure Rust is properly installed](/sdk-and-tools/troubleshooting/rust-setup)**. +If so, **[make sure Rust is properly installed](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta)**. Then, restart VSCode. Now, `rust-analyzer` should work properly. If the problem persists, please [contact us](/developers/overview). diff --git a/docs/sdk-and-tools/troubleshooting/rust-setup.md b/docs/sdk-and-tools/troubleshooting/rust-setup.md index 665ff7a1d..40f7a5983 100644 --- a/docs/sdk-and-tools/troubleshooting/rust-setup.md +++ b/docs/sdk-and-tools/troubleshooting/rust-setup.md @@ -1,5 +1,5 @@ --- -id: rust-setup +id: fix-rust-setup title: Fix Rust installation --- @@ -37,7 +37,7 @@ We never recommend installing Rust using `brew`, especially because it makes it ::: If you've installed Rust using `snap`: - + ```bash snap remove rustup ``` @@ -54,113 +54,6 @@ If you've installed Rust using `mxpy v9` or later: rustup self uninstall ``` -[comment]: # (mx-context-auto) - -## Installing Rust and sc-meta - -:::note -`sc-meta` is universal smart contract management tool. Please follow [this](/developers/meta/sc-meta) for more information. -::: - -[comment]: # (mx-context-auto) - -On Ubuntu (or Windows with WSL), you might need to install the following dependencies of Rust and `sc-meta` first: - -```bash -sudo apt-get install build-essential pkg-config libssl-dev -``` - -Install Rust as recommended on [rust-lang.org](https://www.rust-lang.org/tools/install): - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -``` - -Then, choose **Proceed with installation (default)**. +## Reinstall Toolchain Setup -:::tip -Generally speaking, you should install Rust `v1.78.0` (stable channel) or later, or `nightly-2024-05-22` (nightly channel) or later. -::: - -```bash -rustup update -rustup default stable -``` - -Afterwards, open a new terminal (shell) and install `sc-meta`: - -```bash -cargo install multiversx-sc-meta --locked -``` - -Once `sc-meta` is ready, install the `wasm32` target (for the Rust compiler), `wasm-opt`, and others dependencies as follows: - -```bash -# Installs `wasm32`, `wasm-opt`, and others in one go: -sc-meta install all - -cargo install twiggy -``` - -[comment]: # (mx-context-auto) - -### Within CI / CD - -On Ubuntu (or Windows with WSL), you might need to install the following dependencies of Rust and `sc-meta` first: - -```bash -sudo apt-get install build-essential pkg-config libssl-dev -``` - -For CI / CD, install Rust as follows: - -```bash -wget -O rustup.sh https://sh.rustup.rs && \ - chmod +x rustup.sh && \ - ./rustup.sh --verbose --default-toolchain stable --target wasm32-unknown-unknown -y - -cargo install multiversx-sc-meta --locked -``` - -[comment]: # (mx-context-auto) - -### Handle missing dependencies - -On Ubuntu (or Windows with WSL), you might need to install the following dependencies of Rust and `sc-meta` before installing Rust: - -```bash -sudo apt-get install build-essential pkg-config libssl-dev -``` - -Also see this [GitHub issue](https://github.com/multiversx/mx-sdk-py-cli/issues/338). - -[comment]: # (mx-context-auto) - -## Check your Rust installation - -You can check your Rust installation by invoking `rustup show`: - -``` -$ rustup show - -Default host: x86_64-unknown-linux-gnu -rustup home: /home/ubuntu/.rustup - -installed toolchains --------------------- - -[...] -stable-x86_64-unknown-linux-gnu (default) - -installed targets for active toolchain --------------------------------------- - -[...] -wasm32-unknown-unknown - -active toolchain ----------------- - -[...] -stable-x86_64-unknown-linux-gnu (default) -``` +After successfully uninstalling your current toolchain, you can follow the [setup guide](/docs/developers/toolchain-setup.md) to reinstall the environment from scratch. diff --git a/docs/sdk-and-tools/troubleshooting/troubleshooting.md b/docs/sdk-and-tools/troubleshooting/troubleshooting.md index 427a67976..8e507b000 100644 --- a/docs/sdk-and-tools/troubleshooting/troubleshooting.md +++ b/docs/sdk-and-tools/troubleshooting/troubleshooting.md @@ -7,5 +7,5 @@ title: Overview Here you can find some common issues and their solutions, in the context of [MultiversX SDKs and Tools](/sdk-and-tools/overview). -1. [Fix Rust installation](/sdk-and-tools/troubleshooting/rust-setup) +1. [Fix Rust installation](/sdk-and-tools/troubleshooting/fix-rust-setup) 2. [Fix IDEs configuration](/sdk-and-tools/troubleshooting/ide-setup) diff --git a/sidebars.js b/sidebars.js index 6020656fa..926fbe639 100644 --- a/sidebars.js +++ b/sidebars.js @@ -76,6 +76,7 @@ const sidebars = { label: "Rust Development Framework", items: [ "developers/smart-contracts", + "developers/toolchain-setup", { type: "category", label: "Rust Developer reference", @@ -142,7 +143,7 @@ const sidebars = { "developers/meta/sc-config", "developers/meta/sc-meta-cli", "developers/meta/sc-allocator", - "developers/meta/rust-nightly", + "developers/meta/rust-stable-vs-nightly", ], }, { @@ -333,7 +334,7 @@ const sidebars = { label: "Troubleshooting", items: [ "sdk-and-tools/troubleshooting/troubleshooting", - "sdk-and-tools/troubleshooting/rust-setup", + "sdk-and-tools/troubleshooting/fix-rust-setup", "sdk-and-tools/troubleshooting/ide-setup", "sdk-and-tools/troubleshooting/multiplatform", ], From 51fcc8d41d12156e97b277a68fc4f153ecb4ee57 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Wed, 15 Oct 2025 14:38:11 +0300 Subject: [PATCH 02/15] sdk-rs - add rust versions to a table --- .../developers/meta/rust-stable-vs-nightly.md | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/docs/developers/meta/rust-stable-vs-nightly.md b/docs/developers/meta/rust-stable-vs-nightly.md index facd647be..980655032 100644 --- a/docs/developers/meta/rust-stable-vs-nightly.md +++ b/docs/developers/meta/rust-stable-vs-nightly.md @@ -19,17 +19,32 @@ For everything after v0.50.0 we recommend running the latest stable version of R Also, everything on versions older than v0.50.0 needs to run on nightly Rust. -:::important - -- For Versions Prior to `v0.50`: - - **Requirement**: A specific nightly build is necessary. - - **Known Working Builds**:`nightly-2023-12-11` and `nightly-2024-05-22` are confirmed to run correctly. -- For Versions `v0.50` through `v0.57`: - - **Requirement**: The **stable channel** must be used, requiring a Rust version that is **greater than or equal** to `1.78` and **less than or equal** to `1.86`. -- For Version `v0.58` and Higher: - - **Requirement**: The **stable channel** must be used, requiring a **minimum** Rust version of `1.78` or newer. - -::: + + + + + + + + + + + + + + + + + + + + + + + + + +
Application VersionRequired Rust ChannelVersion Requirements
Prior to `v0.50`Nightly`nightly-2023-12-11` or `nightly-2024-05-22`
`v0.50` to `v0.57`Stable≥`1.78` and ≤`1.86`
`v0.58` and HigherStable≥`1.85`
[comment]: # (mx-context-auto) From 887744dd189e9b7cb47cd74f812cca952336d9ac Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Wed, 15 Oct 2025 14:50:24 +0300 Subject: [PATCH 03/15] sdk-rs - change rust versions --- docs/developers/meta/rust-stable-vs-nightly.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/developers/meta/rust-stable-vs-nightly.md b/docs/developers/meta/rust-stable-vs-nightly.md index 980655032..d1c121b54 100644 --- a/docs/developers/meta/rust-stable-vs-nightly.md +++ b/docs/developers/meta/rust-stable-vs-nightly.md @@ -34,14 +34,19 @@ Also, everything on versions older than v0.50.0 needs to run on nightly Rust. `nightly-2023-12-11` or `nightly-2024-05-22` - `v0.50` to `v0.57` + `v0.50` to `v0.56` Stable ≥`1.78` and ≤`1.86` + + `v0.57` + Stable + ≥`1.83` and ≤`1.86` + `v0.58` and Higher Stable - ≥`1.85` + ≥`1.83` From 5b57d15b71434d576781b39c6bcac2cb8934aa4f Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Wed, 15 Oct 2025 19:16:18 +0300 Subject: [PATCH 04/15] sdk-rs - add note about wasmer6 --- docs/developers/meta/rust-stable-vs-nightly.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/developers/meta/rust-stable-vs-nightly.md b/docs/developers/meta/rust-stable-vs-nightly.md index d1c121b54..4143aee4d 100644 --- a/docs/developers/meta/rust-stable-vs-nightly.md +++ b/docs/developers/meta/rust-stable-vs-nightly.md @@ -35,22 +35,28 @@ Also, everything on versions older than v0.50.0 needs to run on nightly Rust. `v0.50` to `v0.56` - Stable + **Stable (recommended)** or Nightly ≥`1.78` and ≤`1.86` `v0.57` - Stable + **Stable (recommended)** or Nightly ≥`1.83` and ≤`1.86` `v0.58` and Higher - Stable - ≥`1.83` + **Stable (recommended)** or Nightly + ≥`1.83`* +\* Starting with Rust version `1.89` and higher, there are known runtime issues when using **wasmer 6.0** (`wasmer-experimental`) exclusively on the **Linux platform**. + +:::note +If you are using **wasmer 6.0** on Linux, we recommend pinning your Rust version below `1.89`. +::: + [comment]: # (mx-context-auto) ## Why Nightly for the older versions? From ec57cb743b9a1b5c2fa308a61c2b25a62b8542af Mon Sep 17 00:00:00 2001 From: Gutica Stefan <123564494+stefangutica@users.noreply.github.com> Date: Thu, 16 Oct 2025 10:53:51 +0300 Subject: [PATCH 05/15] Change type field value from 'normal' to 'unsigned' Updated the type field value for fetching smart contract results. --- docs/sdk-and-tools/indices/scresults.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdk-and-tools/indices/scresults.md b/docs/sdk-and-tools/indices/scresults.md index 288f4a4f6..cf685c872 100644 --- a/docs/sdk-and-tools/indices/scresults.md +++ b/docs/sdk-and-tools/indices/scresults.md @@ -15,7 +15,7 @@ This page describes the structure of the `sc-results` index (Elasticsearch), and **The `scresults` index will be deprecated and removed in the near future.** We recommend using the [operations](/sdk-and-tools/indices/es-index-operations) index, which contains all the smart contract results data. -The only change required in your queries is to include the `type` field with the value `normal` to fetch all smart contract results. +The only change required in your queries is to include the `type` field with the value `unsigned` to fetch all smart contract results. Please make the necessary updates to ensure a smooth transition. If you need further assistance, feel free to reach out. From e9b7dca4a0088163c5d029bf10b91876f340add4 Mon Sep 17 00:00:00 2001 From: Bubu <2005901+BubuMVX@users.noreply.github.com> Date: Thu, 16 Oct 2025 12:25:12 +0200 Subject: [PATCH 06/15] Add Kepler to the External Providers --- docs/sdk-and-tools/rest-api/multiversx-api.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/sdk-and-tools/rest-api/multiversx-api.md b/docs/sdk-and-tools/rest-api/multiversx-api.md index ef61e14a8..1b4e0a386 100644 --- a/docs/sdk-and-tools/rest-api/multiversx-api.md +++ b/docs/sdk-and-tools/rest-api/multiversx-api.md @@ -34,6 +34,14 @@ Checkout information about [pricing](https://blastapi.io/pricing) and API [limit More details on how to get your private endpoint can be found [here](https://docs.blastapi.io/blast-documentation/tutorials-and-guides/using-blast-to-get-a-blockchain-endpoint-1). +**Kepler** + +High-performance infrastructure layer purpose-built for the MultiversX ecosystem. + +Affordable plans, high limits for API, Gateway, Elastic Search, Event Notifier, and many more! + +[https://kepler.projectx.mx](https://kepler.projectx.mx) + [comment]: # (mx-context-auto) ## Dependencies From e879c87dddba9a3ad65ffd0f99815449b0c17c04 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Fri, 17 Oct 2025 13:32:41 +0300 Subject: [PATCH 07/15] rename with rust version --- .../meta/{rust-stable-vs-nightly.md => rust-version.md} | 4 ++-- sidebars.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename docs/developers/meta/{rust-stable-vs-nightly.md => rust-version.md} (97%) diff --git a/docs/developers/meta/rust-stable-vs-nightly.md b/docs/developers/meta/rust-version.md similarity index 97% rename from docs/developers/meta/rust-stable-vs-nightly.md rename to docs/developers/meta/rust-version.md index 4143aee4d..050d3bac1 100644 --- a/docs/developers/meta/rust-stable-vs-nightly.md +++ b/docs/developers/meta/rust-version.md @@ -1,6 +1,6 @@ --- -id: rust-stable-vs-nightly -title: Stable vs. Nightly Rust +id: rust-version +title: Rust Version --- [comment]: # (mx-abstract) diff --git a/sidebars.js b/sidebars.js index 926fbe639..7fb5b32c9 100644 --- a/sidebars.js +++ b/sidebars.js @@ -143,7 +143,7 @@ const sidebars = { "developers/meta/sc-config", "developers/meta/sc-meta-cli", "developers/meta/sc-allocator", - "developers/meta/rust-stable-vs-nightly", + "developers/meta/rust-version", ], }, { From b7204e5569a5a2357592631719e8fdf48462dd18 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 17 Oct 2025 17:18:31 +0300 Subject: [PATCH 08/15] rust version redirect --- docusaurus.config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docusaurus.config.js b/docusaurus.config.js index 5cc328f1d..e506f8458 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -458,6 +458,10 @@ const config = { from: "/developers/log-events/system-delegation-events", to: "/developers/event-logs/system-delegation-events", }, + { + from: "/developers/meta/rust-stable-vs-nightly", + to: "/developers/meta/rust-version", + }, ], createRedirects(existingPath) { return undefined; // Return a falsy value: no redirect created From 585226c3c7978304cabe62dea0895958f4a18817 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Mon, 20 Oct 2025 21:52:28 +0300 Subject: [PATCH 09/15] update info - Smart contract interactions --- .../mxpy/smart-contract-interactions.md | 194 +++++++++--------- sidebars.js | 1 + 2 files changed, 102 insertions(+), 93 deletions(-) diff --git a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md index 72caa0589..f16875777 100644 --- a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md +++ b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md @@ -5,48 +5,50 @@ title: Smart contract interactions [comment]: # (mx-abstract) -Let's dive deeper into the Smart Contract interactions and what do you need to know when you need to interact with a SC. If you followed the previous mxpy related documentation, you should be able to set up your prerequisites like proxy URL, the chain ID and the PEM file. -For this, we need an interactions file. Usually, we find this file inside the contract's folder, in an **interaction** folder. The interactions file usually has a suggestive name, related to which chain the setup has been done. For example: **devnet.snippets.sh**. +Let's dive deeper into the Smart Contract interactions and what do you need to know when you need to interact with a SC. If you followed the [previous `mxpy`](/docs/sdk-and-tools/mxpy/mxpy-cli.md) related documentation, you should be able to set up your prerequisites like proxy URL, the chain ID and the PEM file. + +For this, we need a file inside the contract's folder, with a suggestive name. For example: **devnet.snippets.sh**. :::important -In order to be able to call methods from the interactions file, we need to assign the shell file as a source file in the terminal. We can do this by running the `source devnet.snippets.sh` command. Also, after each change to the interactions file structure, we need to repeat the source command. +In order to be able to call methods from the file, we need to assign the shell file as a source file in the terminal. We can do this by running the `source devnet.snippets.sh` command. Also, after each change to the interactions file, we need to repeat the source command. ::: Let's take the following example: -- We want to deploy a new SC on the Devnet -- We then need to upgrade the contract, to make it payable -- We call an endpoint without transferring any assets -- We make an ESDTTransfer, in order to call a payable endpoint -- We call a view function +1. We want to deploy a new SC on the Devnet. +2. We then need to upgrade the contract, to make it payable. +3. We call an endpoint without transferring any assets. +4. We make an `ESDTTransfer`, in order to call a payable endpoint. +5. We call a view function. [comment]: # (mx-context-auto) ## Prerequisites -[comment]: # (mx-context-auto) - -### mxpy - -We're going to use [**mxpy**](/sdk-and-tools/mxpy/mxpy-cli) to deploy the contract. Follow the installation guide [here](/sdk-and-tools/mxpy/installing-mxpy) - make sure to use the latest version available. - -[comment]: # (mx-context-auto) +:::important +Before starting this tutorial, make sure you have the following: -### Rust +- [`mxpy`](/sdk-and-tools/mxpy/mxpy-cli). Follow the [installation guide](/sdk-and-tools/mxpy/installing-mxpy) - make sure to use the latest version available. +- `stable` **Rust** version `≥ 1.85.0`. Follow the [installation guide](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta). +- `sc-meta` (install [multiversx-sc-meta](/docs/developers/meta/sc-meta-cli.md)) -Install **Rust** and [**sc-meta**](/developers/meta/sc-meta) as depicted [here](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta). They are required to build smart contracts. +::: -## Deploy & Upgrade +## Deploy -First things first. In order to deploy a new contract, we need to use **sc-meta** to build it, by invoking `sc-meta all build`. This will output the WASM bytecode, to be used within the interactions file: +First things first. In order to deploy a new contract, we need to use **sc-meta** to build it, in the contract root, by invoking `sc-meta all build`. This will output the WASM bytecode, to be used within the interactions file: -``` +```shell WASM_PATH="~/my-contract/output/my-contract.wasm" ``` -Now, in order to deploy the contract, we use the special **deploy** function of mxpy, that deploys the contract on the appointed chain, and runs the **init** function of the contract. +Now, in order to deploy the contract, we use the special **deploy** function of `mxpy`, that deploys the contract on the appointed chain, and runs the **init** function of the contract. + +```shell +WALLET_PEM="~/my-wallet/my-wallet.pem" +PROXY="https://devnet-gateway.multiversx.com" +CHAIN_ID="D" -``` deploySC() { mxpy --verbose contract deploy \ --bytecode=${WASM_PATH} \ @@ -56,17 +58,23 @@ deploySC() { --arguments $1 $2 \ --send || return } + +deploySC $1 $2 ``` -Now let's look at the structure of the interaction. It receives the path of the wasm file, where we previously built the contract. It also receives the path of the PEM file, the proxy url and the chain id, where the contract will be deployed. Another important parameter is the gas limit, where we state the maximum amount of gas we are willing to spend with this transaction. Each transaction cost depends on its complexity and the amount of data storage it handles. +Run in terminal the following command to deploy the smart contract on Devnet: + +```shell +source devnet.snippets.sh +``` -Another argument we must take a closer look at is **recall-nonce**. As we know, each account has its own nonce, that increases with each sent transaction. That being said, when calling an endpoint or a deploy function and so on, we must pass the next-in-line nonce, for the transaction to be correctly processed. And **recall-nonce** does just that. It gives us the correct nonce by querying the blockchain for the last one. +Now let's look at the structure of the interaction. It receives the path of the **wasm** file, where we previously built the contract. It also receives the path of the **wallet** (the PEM file), the **proxy URL** and the **chain ID**, where the contract will be deployed. Another important parameter is the **gas limit**, where we state the maximum amount of gas we are willing to spend with this transaction. Each transaction cost depends on its complexity and the amount of data storage it handles. Other than this, we also have the **arguments** keyword, that allows us to pass in the required parameters. As we previously said, deploying a smart contract means that we run the **init** function, which may or may not request some parameters. In our case, the **init** function has two different arguments, and we pass them when calling the **deploy** function. We'll come back later in this section at how we can pass parameters in function calls. -After the transaction is sent, mxpy will output information like the transaction hash, data and any other important information, based on the type of transaction. In case of a contract deployment, it will also output the newly deployed contract address. +After the transaction is sent, `mxpy` will output information like the transaction hash, data and any other important information, based on the type of transaction. In case of a contract deployment, it will also output the newly deployed contract address. -Let's now suppose we need to make the contract payable, in case it needs to receive funds. We could redeploy the contract but that will mean two different contracts, and not to mention that we will lose any existing storage. For that, we can use the **upgrade** command, that replaces the existing SC bytecode with the newly built contract version. +Let's now suppose we need to make the contract **payable**, in case it needs to receive funds. We could redeploy the contract but that will mean two different contracts, and not to mention that we will lose any existing storage. For that, we can use the **upgrade** command, that replaces the existing SC bytecode with the newly built contract version. :::caution It is import to handle data storage with caution when upgrading a smart contract. Data structure, especially for complex data types, must be preserved, otherwise the data may become corrupt. @@ -74,9 +82,11 @@ It is import to handle data storage with caution when upgrading a smart contract The upgrade function would look like this: -``` +```shell +CONTRACT_ADDRESS="erd1qqqqqqqqqqqqqpgqspymnxmfjve0vxhmep5vr3tf6sj8e80dd8ss2eyn3p" + upgradeSC() { - mxpy --verbose contract upgrade ${CONTRACT_ADDRESS} --payable \ + mxpy --verbose contract upgrade ${CONTRACT_ADDRESS} --metadata-payable \ --bytecode=${WASM_PATH} \ --pem=${WALLET_PEM} \ --gas-limit=60000000 \ @@ -84,32 +94,25 @@ upgradeSC() { --arguments $1 $2 \ --send || return } -``` -:::important -When we run the **upgrade** function, we once again call the **init** function of the SC. What this mean is that we must pass the function's parameters again, no matter if they changed or if they remained the same. -::: +upgradeSC $1 $2 +``` -Here we have 2 new different elements that we need to observe. First, we changed the **deploy** function with the **upgrade** function, which in turn requires the address of the previously deployed SC address, in order to be able to identify what SC to upgrade. Is important to note that this function can only be called by the SC's owner. The second element we need to observe is the **payable** keyword, which represents a code metadata flag that allows the SC to receive payments. - -:::tip -More information about Code Metadata can be found [here](/developers/data/code-metadata). -::: +Here we have 2 new different elements that we need to observe. First, we changed the **deploy** function with the **upgrade** function, which in turn requires the address of the previously deployed SC address, in order to be able to identify what SC to upgrade. Is important to note that this function can only be called by the SC's owner. The second element we need to observe is the **metadata-payable** keyword, which represents a [code metadata](/docs/developers/data/code-metadata.md) flag that allows the SC to receive payments. [comment]: # (mx-context-auto) ## Non payable endpoint interaction -Let's suppose we want to call the following endpoint, that receives an address and three different BigUint arguments, in this specific order. - -``` +Let's suppose we want to call the following endpoint, that receives an address and three different `BigUint` arguments, in this specific order. +```shell ###PARAMS #1 - FirstBigUintArgument #2 - SecondBigUintArgument - -ADDRESS_ARGUMENT="erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3" THIRD_BIGUINT_ARGUMENT=0x0f4240 +ADDRESS_ARGUMENT=addr:erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3 + myNonPayableEndpoint() { address_argument="0x$(mxpy wallet bech32 --decode ${ADDRESS_ARGUMENT})" mxpy --verbose contract call ${CONTRACT_ADDRESS} \ @@ -120,57 +123,59 @@ myNonPayableEndpoint() { --arguments $address_argument $1 $2 ${THIRD_BIGUINT_ARGUMENT}\ --send || return } + +myNonPayableEndpoint $1 $2 ``` -So, what happens in this interaction and how do we call it? Besides the function and arguments parts, the snippet is more or less the same as when deploying or upgrading a contract. When calling a non payable function, we need to provide the endpoint's name as the function argument. As for the arguments, they have to be in the same order as in the SC, including when calling an endpoint that has a variable number of arguments. Now, for the sake of example, we provided the arguments in multiple ways. It is up to each developer to choose the layout he prefers, but a few points need to be underlined: +So, what happens in this interaction and how do we call it? Besides the function and arguments parts, the snippet is more or less the same as when deploying or upgrading a contract. When calling a non payable function, we need to provide the endpoint's name as the function argument. As for the arguments, they have to be in the **same order** as in the SC, including when calling an endpoint that has a variable number of arguments. Now, for the sake of example, we provided the arguments in multiple ways. It is up to each developer to choose the layout he prefers, but a few points need to be underlined: -- Most of the supplied arguments need to be in the hex format (0x...). -- When converting a value to a hex format, we need to make sure it has an even number of characters. If not, we need to provide an extra 0 in order to make it even. (e.g. The number 911 -> In hex encoding, it is equal to: 38f -> So we need to provide the argument 0x038f). +- Most of the supplied **arguments** need to be in the **hex format**: `0x...`. +- When converting a value to a hex format, we need to make sure it has an **even number** of characters. If not, we need to provide an extra 0 in order to make it even. (e.g. The number 911 -> In hex encoding, it is equal to: 38f -> So we need to provide the argument 0x038f). - Arguments can be provided both as a fixed arguments (usually for unchangeable arguments like the contract's address or a fixed number) or can be provided as an input in the terminal, when interacting with the snippet (mostly used for arguments that change often like numbers). -In our example we provide the address argument as a fixed argument. We then convert it to hex format (as it is in the bech32 format by default) and only after that we pass it as a parameter. As for the BigUint parameters, we provide the first two parameters directly in the terminal and the last one as a fixed argument, hex encoded. +In our example we provide the address argument as a fixed argument. We then convert it to hex format (as it is in the bech32 format by default) and only after that we pass it as a parameter. As for the `BigUint` parameters, we provide the first two parameters directly in the terminal and the last one as a fixed argument, hex encoded. :::tip -mxpy facilitates us with some encoding conventions, including: +`mxpy` facilitates us with some encoding conventions, including: -- We can use **str:** for encoding strings. For example: str:MYTOKEN-123456 -- Blockchain addresses that start with **erd1** are automatically encoded, so there is no need to further hex encode them -- The values **true** or **false** are automatically converted to **boolean** values -- Values that are identified as **numbers** are hex encoded by default -- Arguments like **0x...** are left unchanged, as they are interpreted as already encoded hex values +- We can use **str:** for encoding strings. For example: `str:MYTOKEN-123456`. +- Blockchain addresses that start with **erd1** are automatically encoded, so there is no need to further hex encode them. +- The values **true** or **false** are automatically converted to **boolean** values. +- Values that are identified as **numbers** are hex encoded by default. +- Arguments like `0x...` are left unchanged, as they are interpreted as already encoded hex values. - ::: +::: So, in case of our **myNonPayableEndpoint** interaction, we can write it like so: -``` +```shell ###PARAMS #1 - FirstBigUintArgument #2 - SecondBigUintArgument - -ADDRESS_ARGUMENT="erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3" THIRD_BIGUINT_ARGUMENT=1000000 +ADDRESS_ARGUMENT=addr:erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3 + myNonPayableEndpoint() { mxpy --verbose contract call ${CONTRACT_ADDRESS} \ --pem=${WALLET_PEM} \ --gas-limit=6000000 \ --proxy=${PROXY} --chain=${CHAIN_ID} \ --function="myNonPayableEndpoint" \ - --arguments $address_argument $1 $2 ${THIRD_BIGUINT_ARGUMENT}\ + --arguments ${ADDRESS_ARGUMENT} $1 $2 ${THIRD_BIGUINT_ARGUMENT}\ --send || return } ``` A call example for this endpoint would look like: -``` +```shell myNonPayableEndpoint 10000 100000 ``` -This would translate in (using unencoded values for easier reading): +Using unencoded values (for easier reading) would translate into: -``` +```shell myNonPayableEndpoint erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3 10000 100000 1000000 ``` @@ -188,7 +193,7 @@ It is import to make sure all arguments have the correct encoding. Otherwise, th Now let's take a look at the following example, where we want to call a payable endpoint. -``` +```shell myPayableEndpoint() { method_name=str:myPayableEndpoint my_token=str:$1 @@ -203,7 +208,7 @@ myPayableEndpoint() { } ``` -As we can see, the way we call a payable endpoint is by calling an ESDTTransfer function (or any other function that transfer assets and supports contract calls) and providing the name of the method as an argument. The order of the arguments differs for each transfer function. In our case, we specify in the terminal the token type and the amount of tokens we want to transfer and then we provide as a fixed input what SC function we want to call. +As we can see, the way we call a **payable endpoint** is by calling an `ESDTTransfer` function (or any other function that transfer assets and supports contract calls) and providing the name of the method as an argument. The order of the arguments differs for each transfer function. In our case, we specify in the terminal the **token type** and **the amount of tokens** we want to transfer and then we provide as a **fixed input** what SC function we want to call. [comment]: # (mx-context-auto) @@ -211,7 +216,8 @@ As we can see, the way we call a payable endpoint is by calling an ESDTTransfer Now let's suppose we want to call an endpoint that accepts an NFT or an SFT as payment. -``` +```shell +###PARAMS # $1 = NFT/SFT Token Identifier, # $2 = NFT/SFT Token Nonce, # $3 = NFT/SFT Token Amount, @@ -219,30 +225,31 @@ Now let's suppose we want to call an endpoint that accepts an NFT or an SFT as p FIRST_BIGUINT_ARGUMENT=1000 SECOND_BIGUINT_ARGUMENT=10000 +MY_WALLET_ADDRESS=erd1... + myESDTNFTPayableEndpoint() { - user_address="$(mxpy wallet pem-address $WALLET_PEM)" method_name=str:myESDTNFTPayableEndpoint sft_token=str:$1 sft_token_nonce=$2 sft_token_amount=$3 - destination_address=$4 - mxpy --verbose contract call $user_address \ + destination_address=addr:$4 + mxpy --verbose contract call ${MY_WALLET_ADDRESS} \ --pem=${WALLET_PEM} \ --gas-limit=100000000 \ --proxy=${PROXY} --chain=${CHAIN_ID} \ --function="ESDTNFTTransfer" \ - --arguments $sft_token - $sft_token_nonce - $sft_token_amount - $destination_address - $method_name - ${FIRST_BIGUINT_ARGUMENT} + --arguments $sft_token \ + $sft_token_nonce \ + $sft_token_amount \ + $destination_address \ + $method_name \ + ${FIRST_BIGUINT_ARGUMENT} \ ${SECOND_BIGUINT_ARGUMENT} \ --send || return } ``` -First of all, to call this type of transfer function we need to pass the receiver address the same as the sender address. So in this example we convert the caller's address based on the indicated PEM file. Now, like in the case of `ESDTTransfer`, the name of the called function is `ESDTNFTTransfer`. All the other required data is passed as arguments (including the destination contract's address and the endpoint). In case of this single NFT/SFT transfer, we first pass the token (identifier, nonce and amount) and then we pass the destination address and the name of the endpoint. In the end we pass whatever parameters the indicated method needs. +First of all, to call this type of transfer function we need to pass the receiver address the same as the sender address. So in this example, `MY_WALLET_ADDRESS` is the caller's address of the PEM wallet used. Now, like in the case of `ESDTTransfer`, the name of the called function is `ESDTNFTTransfer`. All the other required data is passed as arguments (including the destination contract's address and the endpoint). In case of this single NFT/SFT transfer, we first pass the **token** (identifier, nonce and amount) and then we pass the **destination address** and the **name of the endpoint**. In the end we pass whatever parameters the indicated method needs. [comment]: # (mx-context-auto) @@ -250,7 +257,7 @@ First of all, to call this type of transfer function we need to pass the receive In case we need to call an endpoint that accepts multiple tokens (let's say for example 2 fungible tokens and an NFT). Let's take a look at the following example: -``` +```shell ###PARAMS # $1 = Destination Address, @@ -265,9 +272,8 @@ In case we need to call an endpoint that accepts multiple tokens (let's say for FIRST_BIGUINT_ARGUMENT=1000 SECOND_BIGUINT_ARGUMENT=10000 myMultiESDTNFTPayableEndpoint() { - user_address="$(mxpy wallet pem-address $WALLET_PEM)" method_name=str:myMultiESDTPayableEndpoint - destination_address=$1 + destination_address=addr:$1 number_of_tokens=3 first_token=str:$2 first_token_nonce=0 @@ -284,25 +290,27 @@ myMultiESDTNFTPayableEndpoint() { --gas-limit=100000000 \ --proxy=${PROXY} --chain=${CHAIN_ID} \ --function="MultiESDTNFTTransfer" \ - --arguments $destination_address - $number_of_tokens - $first_token - $first_token_nonce - $first_token_amount - $second_token - $second_token_nonce - $second_token_amount - $third_token - $third_token_nonce - $third_token_amount - $method_name - ${FIRST_BIGUINT_ARGUMENT} + --arguments $destination_address \ + $number_of_tokens \ + $first_token \ + $first_token_nonce \ + $first_token_amount \ + $second_token \ + $second_token_nonce \ + $second_token_amount \ + $third_token \ + $third_token_nonce \ + $third_token_amount \ + $method_name \ + ${FIRST_BIGUINT_ARGUMENT} \ ${SECOND_BIGUINT_ARGUMENT} \ --send || return } ``` -In this example, we call `myMultiESDTPayableEndpoint` endpoint, by transferring 3 different tokens (the first two are fungible tokens and the last one is an NFT). The endpoint takes 2 BigUInt arguments. The layout of the snippet is almost the same as with **ESDTNFTTransfer** (including the fact that the sender is the same as the receiver) but has different arguments. We now pass the destination address first and the number of ESDT/NFT tokens that we want to sent. Then, for each sent token, we specify the identifier, the nonce (in our example 0 for the fungible tokens and a specific value for the NFT) and the amount. In the end, like with the **ESDTTransfer**, we pass the name of the method we want to call and the rest of the parameters of that specific method. +In this example, we call `myMultiESDTPayableEndpoint` endpoint, by transferring **3 different tokens**: the first two are fungible tokens and the last one is an NFT. + +The endpoint takes 2 BigUInt arguments. The layout of the snippet is almost the same as with **ESDTNFTTransfer** (including the fact that the sender is the same as the receiver) but has different arguments. We now pass the destination address first and the number of ESDT/NFT tokens that we want to sent. Then, for each sent token, we specify the identifier, the nonce (in our example 0 for the fungible tokens and a specific value for the NFT) and the amount. In the end, like with the **ESDTTransfer**, we pass the name of the method we want to call and the rest of the parameters of that specific method. :::tip More information about ESDT Transfers [here](/tokens/fungible-tokens/#transfers). @@ -314,7 +322,7 @@ More information about ESDT Transfers [here](/tokens/fungible-tokens/#transfers) In case we want to call a view function, we can use the **query** keyword. -``` +```shell ###PARAMS #1 - First argument @@ -322,9 +330,9 @@ In case we want to call a view function, we can use the **query** keyword. myView() { mxpy --verbose contract query ${CONTRACT_ADDRESS} \ --proxy=${PROXY} \ - --function="myView" + --function="myView" \ --arguments $1 $2 } ``` -When calling a view function, mxpy will output the standard information in the terminal, along with the results, formatted based on the requested data type. The arguments are specified in the same way as with endpoints. +When calling a **view** function, `mxpy` will output the standard information in the terminal, along with the results, formatted based on the requested data type. The arguments are specified in the same way as with endpoints. diff --git a/sidebars.js b/sidebars.js index 7fb5b32c9..8d343db03 100644 --- a/sidebars.js +++ b/sidebars.js @@ -202,6 +202,7 @@ const sidebars = { items: [ "sdk-and-tools/mxpy/installing-mxpy", "sdk-and-tools/mxpy/mxpy-cli", + "sdk-and-tools/mxpy/smart-contract-interactions", ], }, { From c27745766d342bd340a42a903c7667744cb7bd0c Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Wed, 22 Oct 2025 12:40:12 +0300 Subject: [PATCH 10/15] final touch - Smart contract interactions --- .../mxpy/smart-contract-interactions.md | 95 +++++++++++-------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md index f16875777..da7e364f8 100644 --- a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md +++ b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md @@ -5,12 +5,18 @@ title: Smart contract interactions [comment]: # (mx-abstract) -Let's dive deeper into the Smart Contract interactions and what do you need to know when you need to interact with a SC. If you followed the [previous `mxpy`](/docs/sdk-and-tools/mxpy/mxpy-cli.md) related documentation, you should be able to set up your prerequisites like proxy URL, the chain ID and the PEM file. +Let's dive deeper into smart contract interactions and what you need to know to interact with a contract. If you followed the [previous `mxpy`](/docs/sdk-and-tools/mxpy/mxpy-cli.md) related documentation, you should be able to set up your prerequisites like proxy URL, the chain ID and the PEM file. -For this, we need a file inside the contract's folder, with a suggestive name. For example: **devnet.snippets.sh**. +For this, we need a file inside the contract's folder, with a suggestive name. For example: `devnet.snippets.sh`. :::important -In order to be able to call methods from the file, we need to assign the shell file as a source file in the terminal. We can do this by running the `source devnet.snippets.sh` command. Also, after each change to the interactions file, we need to repeat the source command. +In order to be able to call methods from the file, we need to assign the shell file as a source file in the terminal. We can do this by running the next command: + +```shell +source devnet.snippets.sh +``` + +After each change to the interactions file, we need to repeat the source command. ::: Let's take the following example: @@ -25,18 +31,23 @@ Let's take the following example: ## Prerequisites -:::important Before starting this tutorial, make sure you have the following: - [`mxpy`](/sdk-and-tools/mxpy/mxpy-cli). Follow the [installation guide](/sdk-and-tools/mxpy/installing-mxpy) - make sure to use the latest version available. -- `stable` **Rust** version `≥ 1.85.0`. Follow the [installation guide](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta). -- `sc-meta` (install [multiversx-sc-meta](/docs/developers/meta/sc-meta-cli.md)) +- `stable` **Rust** version `≥ 1.83.0`. Follow the [installation guide](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta). +- `sc-meta` (install [multiversx-sc-meta](/docs/developers/meta/sc-meta-cli.md)). -::: +[comment]: # (mx-context-auto) ## Deploy -First things first. In order to deploy a new contract, we need to use **sc-meta** to build it, in the contract root, by invoking `sc-meta all build`. This will output the WASM bytecode, to be used within the interactions file: +First things first. In order to deploy a new contract, we need to use `sc-meta` to build it, in the contract root, by invoking the next command: + +```shell +sc-meta all build +``` + +This will output the WASM bytecode, to be used within the interactions file: ```shell WASM_PATH="~/my-contract/output/my-contract.wasm" @@ -58,21 +69,24 @@ deploySC() { --arguments $1 $2 \ --send || return } - -deploySC $1 $2 ``` -Run in terminal the following command to deploy the smart contract on Devnet: +Run in terminal the following command to deploy the smart contract on Devnet. Replace `arg1` and `arg2` with your desired deployment values. ```shell source devnet.snippets.sh +deploySC arg1 arg2 ``` -Now let's look at the structure of the interaction. It receives the path of the **wasm** file, where we previously built the contract. It also receives the path of the **wallet** (the PEM file), the **proxy URL** and the **chain ID**, where the contract will be deployed. Another important parameter is the **gas limit**, where we state the maximum amount of gas we are willing to spend with this transaction. Each transaction cost depends on its complexity and the amount of data storage it handles. +Now let's look at the structure of the interaction. It receives the path of the **wasm file**, where we previously built the contract. It also receives the path of the **wallet** (the PEM file), the **proxy URL** and the **chain ID**, where the contract will be deployed. Another important parameter is the **gas limit**, where we state the maximum amount of gas we are willing to spend with this transaction. Each transaction cost depends on its complexity and the amount of data storage it handles. Other than this, we also have the **arguments** keyword, that allows us to pass in the required parameters. As we previously said, deploying a smart contract means that we run the **init** function, which may or may not request some parameters. In our case, the **init** function has two different arguments, and we pass them when calling the **deploy** function. We'll come back later in this section at how we can pass parameters in function calls. -After the transaction is sent, `mxpy` will output information like the transaction hash, data and any other important information, based on the type of transaction. In case of a contract deployment, it will also output the newly deployed contract address. +After the transaction is sent, `mxpy` will output information like the **transaction hash**, **data** and any other important information, based on the type of transaction. In case of a contract deployment, it will also output the newly deployed contract address. + +[comment]: # (mx-context-auto) + +## Upgrade Let's now suppose we need to make the contract **payable**, in case it needs to receive funds. We could redeploy the contract but that will mean two different contracts, and not to mention that we will lose any existing storage. For that, we can use the **upgrade** command, that replaces the existing SC bytecode with the newly built contract version. @@ -94,11 +108,12 @@ upgradeSC() { --arguments $1 $2 \ --send || return } - -upgradeSC $1 $2 ``` -Here we have 2 new different elements that we need to observe. First, we changed the **deploy** function with the **upgrade** function, which in turn requires the address of the previously deployed SC address, in order to be able to identify what SC to upgrade. Is important to note that this function can only be called by the SC's owner. The second element we need to observe is the **metadata-payable** keyword, which represents a [code metadata](/docs/developers/data/code-metadata.md) flag that allows the SC to receive payments. +Here we have 2 new different elements that we need to observe: + +1. We changed the **deploy** function with the **upgrade** function. This new function requires the address of the previously deployed smart contract so the system can identify which contract to update. It is important to note that this function can only be called by the smart contract's owner. +2. The **metadata-payable** keyword, which represents a [code metadata](/docs/developers/data/code-metadata.md) flag that allows the smart contract to receive payments. [comment]: # (mx-context-auto) @@ -108,8 +123,8 @@ Let's suppose we want to call the following endpoint, that receives an address a ```shell ###PARAMS -#1 - FirstBigUintArgument -#2 - SecondBigUintArgument +# $1 = FirstBigUintArgument +# $2 = SecondBigUintArgument THIRD_BIGUINT_ARGUMENT=0x0f4240 ADDRESS_ARGUMENT=addr:erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3 @@ -123,23 +138,26 @@ myNonPayableEndpoint() { --arguments $address_argument $1 $2 ${THIRD_BIGUINT_ARGUMENT}\ --send || return } - -myNonPayableEndpoint $1 $2 ``` -So, what happens in this interaction and how do we call it? Besides the function and arguments parts, the snippet is more or less the same as when deploying or upgrading a contract. When calling a non payable function, we need to provide the endpoint's name as the function argument. As for the arguments, they have to be in the **same order** as in the SC, including when calling an endpoint that has a variable number of arguments. Now, for the sake of example, we provided the arguments in multiple ways. It is up to each developer to choose the layout he prefers, but a few points need to be underlined: +So, what happens in this interaction and how do we call it? -- Most of the supplied **arguments** need to be in the **hex format**: `0x...`. -- When converting a value to a hex format, we need to make sure it has an **even number** of characters. If not, we need to provide an extra 0 in order to make it even. (e.g. The number 911 -> In hex encoding, it is equal to: 38f -> So we need to provide the argument 0x038f). +Besides the function and arguments parts, the snippet is more or less the same as when deploying or upgrading a contract. When calling a non payable function, we need to provide the endpoint's name as the function argument. As for the arguments, they have to be in the **same order** as in the smart contract, including when calling an endpoint that has a variable number of arguments. Now, for the sake of example, we provided the arguments in multiple ways. + +It is up to each developer to choose the layout he prefers, but a few points need to be underlined: + +- Most of the supplied **arguments** need to be in the **hexadecimal format**: `0x...`. +- When converting a value to a hexadecimal format, we need to make sure it has an **even number** of characters. If not, we need to provide an extra `0` in order to make it even: + - Example: the number `911` -> in hexadecimal encoding, it is equal to: `38f` -> so we need to provide the argument `0x038f`. - Arguments can be provided both as a fixed arguments (usually for unchangeable arguments like the contract's address or a fixed number) or can be provided as an input in the terminal, when interacting with the snippet (mostly used for arguments that change often like numbers). -In our example we provide the address argument as a fixed argument. We then convert it to hex format (as it is in the bech32 format by default) and only after that we pass it as a parameter. As for the `BigUint` parameters, we provide the first two parameters directly in the terminal and the last one as a fixed argument, hex encoded. +In our example we provide the address argument as a fixed argument. We then convert it to hexadecimal format (as it is in the bech32 format by default) and only after that we pass it as a parameter. As for the `BigUint` parameters, we provide the first two parameters directly in the terminal and the last one as a fixed argument, hexadecimal encoded. :::tip `mxpy` facilitates us with some encoding conventions, including: -- We can use **str:** for encoding strings. For example: `str:MYTOKEN-123456`. -- Blockchain addresses that start with **erd1** are automatically encoded, so there is no need to further hex encode them. +- We can use `str:` for encoding strings. For example: `str:MYTOKEN-123456`. +- Blockchain addresses that start with `erd1` are automatically encoded, so there is no need to further hex encode them. - The values **true** or **false** are automatically converted to **boolean** values. - Values that are identified as **numbers** are hex encoded by default. - Arguments like `0x...` are left unchanged, as they are interpreted as already encoded hex values. @@ -149,10 +167,9 @@ In our example we provide the address argument as a fixed argument. We then conv So, in case of our **myNonPayableEndpoint** interaction, we can write it like so: ```shell - ###PARAMS -#1 - FirstBigUintArgument -#2 - SecondBigUintArgument +# $1 = FirstBigUintArgument +# $2 = SecondBigUintArgument THIRD_BIGUINT_ARGUMENT=1000000 ADDRESS_ARGUMENT=addr:erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3 @@ -170,6 +187,7 @@ myNonPayableEndpoint() { A call example for this endpoint would look like: ```shell +source devnet.snippets.sh myNonPayableEndpoint 10000 100000 ``` @@ -208,7 +226,7 @@ myPayableEndpoint() { } ``` -As we can see, the way we call a **payable endpoint** is by calling an `ESDTTransfer` function (or any other function that transfer assets and supports contract calls) and providing the name of the method as an argument. The order of the arguments differs for each transfer function. In our case, we specify in the terminal the **token type** and **the amount of tokens** we want to transfer and then we provide as a **fixed input** what SC function we want to call. +As we can see, the way we call a **payable endpoint** is by calling an `ESDTTransfer` function (or any other function that transfer assets and supports contract calls) and providing the name of the method as an argument. The order of the arguments differs for each transfer function. In our case, we specify in the terminal the **token type** and **the amount of tokens** we want to transfer and then we provide as a **fixed input** what smart contract endpoint we want to call. [comment]: # (mx-context-auto) @@ -222,7 +240,6 @@ Now let's suppose we want to call an endpoint that accepts an NFT or an SFT as p # $2 = NFT/SFT Token Nonce, # $3 = NFT/SFT Token Amount, # $4 = Destination Address, - FIRST_BIGUINT_ARGUMENT=1000 SECOND_BIGUINT_ARGUMENT=10000 MY_WALLET_ADDRESS=erd1... @@ -249,7 +266,11 @@ myESDTNFTPayableEndpoint() { } ``` -First of all, to call this type of transfer function we need to pass the receiver address the same as the sender address. So in this example, `MY_WALLET_ADDRESS` is the caller's address of the PEM wallet used. Now, like in the case of `ESDTTransfer`, the name of the called function is `ESDTNFTTransfer`. All the other required data is passed as arguments (including the destination contract's address and the endpoint). In case of this single NFT/SFT transfer, we first pass the **token** (identifier, nonce and amount) and then we pass the **destination address** and the **name of the endpoint**. In the end we pass whatever parameters the indicated method needs. +First of all, to call this type of transfer function we need to pass the receiver address the same as the sender address. So in this example, `MY_WALLET_ADDRESS` is the caller's address of the PEM wallet used. + +Now, like in the case of `ESDTTransfer`, the name of the called function is `ESDTNFTTransfer`. All the other required data is passed as arguments (including the destination contract's address and the endpoint). + +In case of this single NFT/SFT transfer, we first pass the **token** (identifier, nonce and amount) and then we pass the **destination address** and the **name of the endpoint**. In the end we pass whatever parameters the indicated method needs. [comment]: # (mx-context-auto) @@ -268,9 +289,9 @@ In case we need to call an endpoint that accepts multiple tokens (let's say for # $6 = Third Token Identifier, # $7 = Third Token Nonce, # $8 = Third Token Identifier, - FIRST_BIGUINT_ARGUMENT=1000 SECOND_BIGUINT_ARGUMENT=10000 + myMultiESDTNFTPayableEndpoint() { method_name=str:myMultiESDTPayableEndpoint destination_address=addr:$1 @@ -310,7 +331,7 @@ myMultiESDTNFTPayableEndpoint() { In this example, we call `myMultiESDTPayableEndpoint` endpoint, by transferring **3 different tokens**: the first two are fungible tokens and the last one is an NFT. -The endpoint takes 2 BigUInt arguments. The layout of the snippet is almost the same as with **ESDTNFTTransfer** (including the fact that the sender is the same as the receiver) but has different arguments. We now pass the destination address first and the number of ESDT/NFT tokens that we want to sent. Then, for each sent token, we specify the identifier, the nonce (in our example 0 for the fungible tokens and a specific value for the NFT) and the amount. In the end, like with the **ESDTTransfer**, we pass the name of the method we want to call and the rest of the parameters of that specific method. +The endpoint takes 2 BigUInt arguments. The layout of the snippet is almost the same as with `ESDTNFTTransfer` (including the fact that the sender is the same as the receiver) but has different arguments. We now pass the destination address first and the number of ESDT/NFT tokens that we want to sent. Then, for each sent token, we specify the identifier, the nonce (in our example 0 for the fungible tokens and a specific value for the NFT) and the amount. In the end, like with the `ESDTTransfer`, we pass the name of the method we want to call and the rest of the parameters of that specific method. :::tip More information about ESDT Transfers [here](/tokens/fungible-tokens/#transfers). @@ -323,10 +344,10 @@ More information about ESDT Transfers [here](/tokens/fungible-tokens/#transfers) In case we want to call a view function, we can use the **query** keyword. ```shell - ###PARAMS -#1 - First argument -#2 - Second argument +# $1 = First argument +# $2 = Second argument + myView() { mxpy --verbose contract query ${CONTRACT_ADDRESS} \ --proxy=${PROXY} \ From b3a82cb1685610875ece95dd5ec2993941732bd0 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Wed, 22 Oct 2025 12:41:39 +0300 Subject: [PATCH 11/15] remove abreviation --- docs/sdk-and-tools/mxpy/smart-contract-interactions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md index da7e364f8..70611f78e 100644 --- a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md +++ b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md @@ -21,7 +21,7 @@ After each change to the interactions file, we need to repeat the source command Let's take the following example: -1. We want to deploy a new SC on the Devnet. +1. We want to deploy a new smart contract on the Devnet. 2. We then need to upgrade the contract, to make it payable. 3. We call an endpoint without transferring any assets. 4. We make an `ESDTTransfer`, in order to call a payable endpoint. @@ -88,7 +88,7 @@ After the transaction is sent, `mxpy` will output information like the **transac ## Upgrade -Let's now suppose we need to make the contract **payable**, in case it needs to receive funds. We could redeploy the contract but that will mean two different contracts, and not to mention that we will lose any existing storage. For that, we can use the **upgrade** command, that replaces the existing SC bytecode with the newly built contract version. +Let's now suppose we need to make the contract **payable**, in case it needs to receive funds. We could redeploy the contract but that will mean two different contracts, and not to mention that we will lose any existing storage. For that, we can use the **upgrade** command, that replaces the existing smart contract bytecode with the newly built contract version. :::caution It is import to handle data storage with caution when upgrading a smart contract. Data structure, especially for complex data types, must be preserved, otherwise the data may become corrupt. From cd20fc60f73aefd0ce2cf507de74284bee105541 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 28 Oct 2025 13:02:03 +0200 Subject: [PATCH 12/15] apply reviews --- .../mxpy/smart-contract-interactions.md | 157 +++++++----------- 1 file changed, 61 insertions(+), 96 deletions(-) diff --git a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md index 70611f78e..8670b6c58 100644 --- a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md +++ b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md @@ -21,11 +21,11 @@ After each change to the interactions file, we need to repeat the source command Let's take the following example: -1. We want to deploy a new smart contract on the Devnet. -2. We then need to upgrade the contract, to make it payable. -3. We call an endpoint without transferring any assets. -4. We make an `ESDTTransfer`, in order to call a payable endpoint. -5. We call a view function. +1. We want to **deploy** a new smart contract on the Devnet. +2. We then need to **upgrade** the contract, to make it payable. +3. We **call** an endpoint without transferring any assets. +4. We **transfer** ESDT, in order to call a payable endpoint. +5. We call a **view** function. [comment]: # (mx-context-auto) @@ -35,7 +35,7 @@ Before starting this tutorial, make sure you have the following: - [`mxpy`](/sdk-and-tools/mxpy/mxpy-cli). Follow the [installation guide](/sdk-and-tools/mxpy/installing-mxpy) - make sure to use the latest version available. - `stable` **Rust** version `≥ 1.83.0`. Follow the [installation guide](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta). -- `sc-meta` (install [multiversx-sc-meta](/docs/developers/meta/sc-meta-cli.md)). +- `sc-meta`. Follow the [installation guide](/docs/developers/toolchain-setup.md#installing-rust-and-sc-meta). [comment]: # (mx-context-auto) @@ -58,14 +58,12 @@ Now, in order to deploy the contract, we use the special **deploy** function of ```shell WALLET_PEM="~/my-wallet/my-wallet.pem" PROXY="https://devnet-gateway.multiversx.com" -CHAIN_ID="D" deploySC() { mxpy --verbose contract deploy \ --bytecode=${WASM_PATH} \ --pem=${WALLET_PEM} \ - --gas-limit=60000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ + --proxy=${PROXY} \ --arguments $1 $2 \ --send || return } @@ -78,11 +76,11 @@ source devnet.snippets.sh deploySC arg1 arg2 ``` -Now let's look at the structure of the interaction. It receives the path of the **wasm file**, where we previously built the contract. It also receives the path of the **wallet** (the PEM file), the **proxy URL** and the **chain ID**, where the contract will be deployed. Another important parameter is the **gas limit**, where we state the maximum amount of gas we are willing to spend with this transaction. Each transaction cost depends on its complexity and the amount of data storage it handles. +Now let's look at the structure of the interaction. It receives the path of the **wasm file**, where we previously built the contract. It also receives the path of the **wallet** (the PEM file) and the **proxy URL** where the contract will be deployed. Other than this, we also have the **arguments** keyword, that allows us to pass in the required parameters. As we previously said, deploying a smart contract means that we run the **init** function, which may or may not request some parameters. In our case, the **init** function has two different arguments, and we pass them when calling the **deploy** function. We'll come back later in this section at how we can pass parameters in function calls. -After the transaction is sent, `mxpy` will output information like the **transaction hash**, **data** and any other important information, based on the type of transaction. In case of a contract deployment, it will also output the newly deployed contract address. +After the transaction is sent, `mxpy` will output information like the **transaction hash**, **data** and any other important information, based on the type of transaction. In case of a contract deployment, it will also output the **newly deployed contract address**. [comment]: # (mx-context-auto) @@ -103,13 +101,14 @@ upgradeSC() { mxpy --verbose contract upgrade ${CONTRACT_ADDRESS} --metadata-payable \ --bytecode=${WASM_PATH} \ --pem=${WALLET_PEM} \ - --gas-limit=60000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ + --proxy=${PROXY} \ --arguments $1 $2 \ --send || return } ``` +`CONTRACT_ADDRESS` is a placeholder value which value needs to be replaced with the address previously generated in the deploy action. + Here we have 2 new different elements that we need to observe: 1. We changed the **deploy** function with the **upgrade** function. This new function requires the address of the previously deployed smart contract so the system can identify which contract to update. It is important to note that this function can only be called by the smart contract's owner. @@ -126,14 +125,13 @@ Let's suppose we want to call the following endpoint, that receives an address a # $1 = FirstBigUintArgument # $2 = SecondBigUintArgument THIRD_BIGUINT_ARGUMENT=0x0f4240 -ADDRESS_ARGUMENT=addr:erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3 +ADDRESS_ARGUMENT=erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3 myNonPayableEndpoint() { address_argument="0x$(mxpy wallet bech32 --decode ${ADDRESS_ARGUMENT})" mxpy --verbose contract call ${CONTRACT_ADDRESS} \ --pem=${WALLET_PEM} \ - --gas-limit=6000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ + --proxy=${PROXY} \ --function="myNonPayableEndpoint" \ --arguments $address_argument $1 $2 ${THIRD_BIGUINT_ARGUMENT}\ --send || return @@ -142,7 +140,7 @@ myNonPayableEndpoint() { So, what happens in this interaction and how do we call it? -Besides the function and arguments parts, the snippet is more or less the same as when deploying or upgrading a contract. When calling a non payable function, we need to provide the endpoint's name as the function argument. As for the arguments, they have to be in the **same order** as in the smart contract, including when calling an endpoint that has a variable number of arguments. Now, for the sake of example, we provided the arguments in multiple ways. +Besides the function and arguments parts, the snippet is more or less the same as when deploying or upgrading a contract. When calling a **non payable** function, we need to provide the **endpoint's name** as the function argument. As for the arguments, they have to be in the **same order** as in the smart contract, including when calling an endpoint that has a variable number of arguments. Now, for the sake of example, we provided the arguments in multiple ways. It is up to each developer to choose the layout he prefers, but a few points need to be underlined: @@ -159,7 +157,7 @@ In our example we provide the address argument as a fixed argument. We then conv - We can use `str:` for encoding strings. For example: `str:MYTOKEN-123456`. - Blockchain addresses that start with `erd1` are automatically encoded, so there is no need to further hex encode them. - The values **true** or **false** are automatically converted to **boolean** values. -- Values that are identified as **numbers** are hex encoded by default. +- Values that are identified as **numbers** are hex encoded as `BigUint` values. - Arguments like `0x...` are left unchanged, as they are interpreted as already encoded hex values. ::: @@ -176,8 +174,7 @@ ADDRESS_ARGUMENT=addr:erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49 myNonPayableEndpoint() { mxpy --verbose contract call ${CONTRACT_ADDRESS} \ --pem=${WALLET_PEM} \ - --gas-limit=6000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ + --proxy=${PROXY} \ --function="myNonPayableEndpoint" \ --arguments ${ADDRESS_ARGUMENT} $1 $2 ${THIRD_BIGUINT_ARGUMENT}\ --send || return @@ -194,7 +191,7 @@ myNonPayableEndpoint 10000 100000 Using unencoded values (for easier reading) would translate into: ```shell -myNonPayableEndpoint erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3 10000 100000 1000000 +myNonPayableEndpoint addr:erd14nw9pukqyqu75gj0shm8upsegjft8l0awjefp877phfx74775dsq49swp3 10000 100000 1000000 ``` :::caution @@ -213,20 +210,29 @@ Now let's take a look at the following example, where we want to call a payable ```shell myPayableEndpoint() { - method_name=str:myPayableEndpoint - my_token=str:$1 + token_identifier=$1 token_amount=$2 mxpy --verbose contract call ${CONTRACT_ADDRESS} \ --pem=${WALLET_PEM} \ - --gas-limit=6000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ - --function="ESDTTransfer" \ - --arguments $my_token $token_amount $method_name\ + --proxy=${PROXY} \ + --token-transfers $token_identifier $token_amount \ + --function="myPayableEndpoint" \ --send || return } ``` -As we can see, the way we call a **payable endpoint** is by calling an `ESDTTransfer` function (or any other function that transfer assets and supports contract calls) and providing the name of the method as an argument. The order of the arguments differs for each transfer function. In our case, we specify in the terminal the **token type** and **the amount of tokens** we want to transfer and then we provide as a **fixed input** what smart contract endpoint we want to call. +To call a **payable endpoint**, we use the `--token-transfer` flag, which requires two values: + +1. The token identifier. +2. The amount. + +In our case, we specify in the terminal the **token identifier** and **the amount of tokens** we want to transfer. + +:::info +When specifying the amount of tokens to transfer, the value must include the token's decimal precision. + +For example EGLD use 18 decimals. This means that if you want to transfer 1.5 EGLD, the amount value will be $1.5 \times 10^{18}$. +::: [comment]: # (mx-context-auto) @@ -237,41 +243,23 @@ Now let's suppose we want to call an endpoint that accepts an NFT or an SFT as p ```shell ###PARAMS # $1 = NFT/SFT Token Identifier, -# $2 = NFT/SFT Token Nonce, -# $3 = NFT/SFT Token Amount, -# $4 = Destination Address, +# $2 = NFT/SFT Token Amount, FIRST_BIGUINT_ARGUMENT=1000 SECOND_BIGUINT_ARGUMENT=10000 -MY_WALLET_ADDRESS=erd1... myESDTNFTPayableEndpoint() { - method_name=str:myESDTNFTPayableEndpoint - sft_token=str:$1 - sft_token_nonce=$2 - sft_token_amount=$3 - destination_address=addr:$4 - mxpy --verbose contract call ${MY_WALLET_ADDRESS} \ + sft_token_identifier=$1 + sft_token_amount=$2 + mxpy --verbose contract call ${CONTRACT_ADDRESS} \ --pem=${WALLET_PEM} \ - --gas-limit=100000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ - --function="ESDTNFTTransfer" \ - --arguments $sft_token \ - $sft_token_nonce \ - $sft_token_amount \ - $destination_address \ - $method_name \ - ${FIRST_BIGUINT_ARGUMENT} \ - ${SECOND_BIGUINT_ARGUMENT} \ + --proxy=${PROXY} \ + --token-transfers $sft_token_identifier $sft_token_amount \ + --function="myESDTNFTPayableEndpoint" \ + --arguments ${FIRST_BIGUINT_ARGUMENT} ${SECOND_BIGUINT_ARGUMENT} \ --send || return } ``` -First of all, to call this type of transfer function we need to pass the receiver address the same as the sender address. So in this example, `MY_WALLET_ADDRESS` is the caller's address of the PEM wallet used. - -Now, like in the case of `ESDTTransfer`, the name of the called function is `ESDTNFTTransfer`. All the other required data is passed as arguments (including the destination contract's address and the endpoint). - -In case of this single NFT/SFT transfer, we first pass the **token** (identifier, nonce and amount) and then we pass the **destination address** and the **name of the endpoint**. In the end we pass whatever parameters the indicated method needs. - [comment]: # (mx-context-auto) ### Multi-ESDT transfer @@ -279,60 +267,37 @@ In case of this single NFT/SFT transfer, we first pass the **token** (identifier In case we need to call an endpoint that accepts multiple tokens (let's say for example 2 fungible tokens and an NFT). Let's take a look at the following example: ```shell - ###PARAMS -# $1 = Destination Address, -# $2 = First Token Identifier, -# $3 = First Token Amount, -# $4 = Second Token Identifier, -# $5 = Second Token Amount, +# $1 = First Token Identifier, +# $2 = First Token Amount, +# $3 = Second Token Identifier, +# $4 = Second Token Amount, +# $5 = Third Token Identifier, # $6 = Third Token Identifier, -# $7 = Third Token Nonce, -# $8 = Third Token Identifier, FIRST_BIGUINT_ARGUMENT=1000 SECOND_BIGUINT_ARGUMENT=10000 myMultiESDTNFTPayableEndpoint() { - method_name=str:myMultiESDTPayableEndpoint - destination_address=addr:$1 - number_of_tokens=3 - first_token=str:$2 - first_token_nonce=0 - first_token_amount=$3 - second_token=str:$4 - second_token_nonce=0 - second_token_amount=$5 - third_token=str:$6 - third_token_nonce=$7 - third_token_amount=$8 - - mxpy --verbose contract call $user_address \ + first_token_identifier=$1 + first_token_amount=$2 + second_token_identifier=$3 + second_token_amount=$4 + third_token_identifier=$5 + third_token_amount=$6 + + mxpy --verbose contract call ${CONTRACT_ADDRESS} \ --pem=${WALLET_PEM} \ - --gas-limit=100000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ - --function="MultiESDTNFTTransfer" \ - --arguments $destination_address \ - $number_of_tokens \ - $first_token \ - $first_token_nonce \ - $first_token_amount \ - $second_token \ - $second_token_nonce \ - $second_token_amount \ - $third_token \ - $third_token_nonce \ - $third_token_amount \ - $method_name \ - ${FIRST_BIGUINT_ARGUMENT} \ - ${SECOND_BIGUINT_ARGUMENT} \ + --proxy=${PROXY} \ + --token-transfers $first_token_identifier $first_token_amount \ + $second_token_identifier $second_token_amount \ + $third_token_identifier $third_token_amount \ + --function="payable_nft_with_args" \ + --arguments ${FIRST_BIGUINT_ARGUMENT} ${SECOND_BIGUINT_ARGUMENT} \ --send || return -} ``` In this example, we call `myMultiESDTPayableEndpoint` endpoint, by transferring **3 different tokens**: the first two are fungible tokens and the last one is an NFT. -The endpoint takes 2 BigUInt arguments. The layout of the snippet is almost the same as with `ESDTNFTTransfer` (including the fact that the sender is the same as the receiver) but has different arguments. We now pass the destination address first and the number of ESDT/NFT tokens that we want to sent. Then, for each sent token, we specify the identifier, the nonce (in our example 0 for the fungible tokens and a specific value for the NFT) and the amount. In the end, like with the `ESDTTransfer`, we pass the name of the method we want to call and the rest of the parameters of that specific method. - :::tip More information about ESDT Transfers [here](/tokens/fungible-tokens/#transfers). ::: From 1adb36ecbffc98a67c7800f841d2fb9fded9c254 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 28 Oct 2025 14:21:16 +0200 Subject: [PATCH 13/15] apply reviews --- docs/sdk-and-tools/mxpy/smart-contract-interactions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md index 8670b6c58..b293abdfb 100644 --- a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md +++ b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md @@ -140,7 +140,7 @@ myNonPayableEndpoint() { So, what happens in this interaction and how do we call it? -Besides the function and arguments parts, the snippet is more or less the same as when deploying or upgrading a contract. When calling a **non payable** function, we need to provide the **endpoint's name** as the function argument. As for the arguments, they have to be in the **same order** as in the smart contract, including when calling an endpoint that has a variable number of arguments. Now, for the sake of example, we provided the arguments in multiple ways. +Besides the function and arguments parts, the snippet is more or less the same as when deploying or upgrading a contract. When calling a **non payable** function, we need to provide the **endpoint's name** as the function argument. As for the arguments, they have to be in the **same order** as in the endpoint's signature. Now, for the sake of example, we provided the arguments in multiple ways. It is up to each developer to choose the layout he prefers, but a few points need to be underlined: @@ -152,7 +152,7 @@ It is up to each developer to choose the layout he prefers, but a few points nee In our example we provide the address argument as a fixed argument. We then convert it to hexadecimal format (as it is in the bech32 format by default) and only after that we pass it as a parameter. As for the `BigUint` parameters, we provide the first two parameters directly in the terminal and the last one as a fixed argument, hexadecimal encoded. :::tip -`mxpy` facilitates us with some encoding conventions, including: +`mxpy` provides the following encoding conventions: - We can use `str:` for encoding strings. For example: `str:MYTOKEN-123456`. - Blockchain addresses that start with `erd1` are automatically encoded, so there is no need to further hex encode them. @@ -273,7 +273,7 @@ In case we need to call an endpoint that accepts multiple tokens (let's say for # $3 = Second Token Identifier, # $4 = Second Token Amount, # $5 = Third Token Identifier, -# $6 = Third Token Identifier, +# $6 = Third Token Amount, FIRST_BIGUINT_ARGUMENT=1000 SECOND_BIGUINT_ARGUMENT=10000 From eaca40c47722c3c9115f69f641e54938afea3d35 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 28 Oct 2025 14:25:01 +0200 Subject: [PATCH 14/15] typo --- docs/sdk-and-tools/mxpy/smart-contract-interactions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md index b293abdfb..c3a2b1370 100644 --- a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md +++ b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md @@ -231,7 +231,7 @@ In our case, we specify in the terminal the **token identifier** and **the amoun :::info When specifying the amount of tokens to transfer, the value must include the token's decimal precision. -For example EGLD use 18 decimals. This means that if you want to transfer 1.5 EGLD, the amount value will be $1.5 \times 10^{18}$. +For example EGLD uses 18 decimals. This means that if you want to transfer 1.5 EGLD, the amount value will be $1.5 \times 10^{18}$. ::: [comment]: # (mx-context-auto) From 91fbb12da9d9af481b4a84669e3c48246a44ed16 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 28 Oct 2025 17:10:07 +0200 Subject: [PATCH 15/15] add extra info about token identifier for token transfer --- .../sdk-and-tools/mxpy/smart-contract-interactions.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md index c3a2b1370..a1de4e252 100644 --- a/docs/sdk-and-tools/mxpy/smart-contract-interactions.md +++ b/docs/sdk-and-tools/mxpy/smart-contract-interactions.md @@ -221,13 +221,22 @@ myPayableEndpoint() { } ``` -To call a **payable endpoint**, we use the `--token-transfer` flag, which requires two values: +To call a **payable endpoint**, we use the `--token-transfer` argument, which requires two values: 1. The token identifier. 2. The amount. In our case, we specify in the terminal the **token identifier** and **the amount of tokens** we want to transfer. +:::info +The format for the token identifier changes based on the type of asset you are sending: + +- **ESDTs Tokens**: Use the **standard** Token Identifier. +- **NFTs and SFTs**: Use the **extended** Token identifier format, which includes the token's nonce. The nonce must be hex-encoded. + - Example: `NFT-123456-0a` (where `0a` is the hex-encoded nonce). + +::: + :::info When specifying the amount of tokens to transfer, the value must include the token's decimal precision.