Skip to main content

Interactors Overview

Overview

Interactors are Rust-based microservices designed to facilitate interactions with smart contracts on the blockchain. They are essential for system testing and managing the setup and execution of smart contracts in a live blockchain environment. Making use of unified syntax, proxies, and the capability to be autogenerated using sc-meta all snippets, interactors streamline the interaction process, providing a quick and efficient solution for system testing, seamlessly integrating with the development workflow.

Key Features

  • System testing: Perform comprehensive scenario-based testing on the actual blockchain to validate contract behavior under real-world conditions.
  • Contract setup and calling: Interactors simplify the process of setting up (deploy/upgrade) and interacting with smart contracts (calling smart contract endpoints, query), making it easier for Rust developers to deploy and manage their contracts on the blockchain.
info

Before using interactors, make sure you have the following setup:

  • Rust programming language installed on your system:
    • get rustup:
      curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    • choose default toolchain
      rustup default stable
    • check installed toolchains
      rustc --version
      rustup show
  • MultiversX Rust framework version 0.50.0 or higher.
  • multiversx-sc-meta tool installed for generating an interactor template.

Workflow

When interacting with a contract from a Rust testing environment, the following steps are typically required:

  1. Build the contract: Compile and build the smart contract.
  2. Create the proxy file: This can be autogenerated using sc-meta all proxy, which requires a properly configured sc-config.toml for custom proxy paths. Adding a proxy path into the sc-config.toml file ensures that a proxy will be created at that path.
  3. Create a new project and import the proxy: Set up a new project and import the generated proxy.
  4. Write code using unified syntax: Utilize the proxy and unified syntax to write the interaction code.
  5. Run the code and send transactions: Execute the code to send transactions, either against the Rust VM or on the actual blockchain.

However, thanks to the snippets generator, the workflow for the Rust interactors is very straightforward. The sc-meta tool automates all the previously mentioned steps, generating the necessary boilerplate code for building an interactor for the current contract.

info

To generate an interactor, simply run sc-meta all snippets in the root folder of the contract.

adder % sc-meta all snippets

This command performs the following actions:

  • Compiles the contract (still needs to be built).
  • Creates a sc-config.toml configuration file. If the file already exists, another proxy path is inserted (the path to the interactor) without changing the previous setup.
  • Generates a proxy based on the configuration path from the file.
  • Creates an async Rust program with a CLI based on the contract endpoints found in the proxy. Returns typed results for further processing. The new project will be under the newly created interactor folder, inside the contract root folder.
note

Make sure to include the newly generated interactor into the existing file hierarchy to be able to compile and run the code.

Scenarios & CLI

Given the customizable nature of real-life scenarios, the generated interactor provides only a minimal starting point for your Rust program. The generated interactions include:

  • Separate functions for calling each endpoint of the smart contract (based on the proxy).
  • A CLI with distinct commands for each specific endpoint call.

In order to run the code and make use of the CLI, simply call cargo run <endpoint_name> in the root of the interactor folder.

interactor % cargo run deploy

If, however, the CLI needs to cover more complex scenarios, the generated async functions can be used directly and composed with each other. If a testing approach is preferred instead of the CLI, then we can make use of the tokio::test feature, and we could write a system test as such:

interact.rs
#[tokio::test]
async fn test_full_adder_scenario() {
let mut contract_interactor = ContractInteract::new().await;

contract_interactor.deploy().await; // deploy adder
contract_interactor.add(2u64).await; // add 2
let sum = contract_interactor.sum().await; // fetch sum

println!("sum is {:#?}", sum);
}

If ran, this test will run the scenario and will produce transactions on the actual blockchain, depending on the environment setup (devnet, testnet and mainnet).

Running the deploy command for every contract will create a new state.toml file, where information about the contract state will be written for consistency.

Traces

To enhance testing support, any instance of the Interactor struct implements the .with_tracer(...) method. This method enables the interactor to use a tracer and output the result to the specified path. A tracer records every action executed through the interactor, mapping each action to a mandos step.

interact.rs
    let mut interactor = Interactor::new(GATEWAY)
.await
.with_tracer("test_trace.scen.json")
.await;

This ensures the tracer is active, and a file containing all the mandos steps for the specific scenario will be created at the specified path. Having a quickly generated mandos test that encompasses the entire scenario is highly beneficial. After generating the mandos test, you can swiftly test the same scenario against both the Rust and GO VMs.