Docs

Docs

  • Develop
  • Validate
  • Integrate
  • Learn

›Mandos tests reference

Welcome to Elrond

  • Welcome to Elrond

Technology

  • Architecture Overview
  • Glossary
  • Entities
  • Chronology
  • Secure Proof of Stake
  • Adaptive State Sharding
  • The Elrond WASM VM
  • Cross Shard Transactions

Wallet

  • Wallets - Overview
  • Web Wallet
  • Maiar Web Wallet Extension
  • Webhooks
  • Ledger

Tokens

  • Native Tokens
  • ESDT tokens
  • NFT tokens

Validators

  • Validators - Overview
  • System Requirements
  • Install a Mainnet Node

    • Scripts & User config
    • Installing a Validator Node
    • Optional Configurations
    • How to use the Docker Image

    Install a Testnet/Devnet Node

    • Scripts & User config
    • Installing a Validator Node
    • Manage a validator node
    • How to use the Docker Image

    Manage your keys

    • Validator Keys
    • Wallet Keys
    • Protecting your keys

    Staking, Unstaking, Unjailing

    • Staking, unstaking and unjailing
    • Staking
    • Unjailing
    • The Staking Smart Contract
  • The Delegation Manager
  • Convert An Existing Validator Into A Staking Pool
  • Merging A Validator Into An Existing Delegation Smart Contract
  • Rating
  • Elrond Node upgrades
  • Node redundancy
  • Import DB
  • Node CLI
  • Node Databases
  • Useful Links & Tools
  • FAQs

Developers

  • Developers - Overview
  • Tutorials

    • Build a dApp in 15 minutes
    • Build a Microservice for your dApp
    • The Crowdfunding Smart Contract (part 1)
    • The Crowdfunding Smart Contract (part 2)
    • The Counter Smart Contract
    • Custom Wallet Connect

    Signing Transactions

    • Signing Transactions
    • Tools for signing
    • Signing programmatically

    Gas and Fees

    • Overview
    • EGLD transfers (move balance transactions)
    • System Smart Contracts
    • User-defined Smart Contracts

    Developer reference

    • The Elrond Serialization Format
    • Smart contract annotations
    • Smart contract modules
    • Smart contract to smart contract calls
    • Smart Contract Developer Best Practices
    • Code Metadata
    • Smart Contract API Functions
    • Storage Mappers
    • Rust Testing Framework
    • Rust Testing Framework Functions Reference
    • Rust Smart Contract Debugging
    • Random Numbers in Smart Contracts

    Developers Best Practices

    • Basics
    • BigUint Operations
    • The dynamic allocation problem
    • Multi-values

    Mandos tests reference

    • Mandos Overview
    • Mandos Structure
    • Mandos Simple Values
    • Mandos Complex Values
    • Embedding Mandos code in Go
  • Constants
  • Built-In Functions
  • Account storage
  • Setup a Local Testnet
  • Set up a Local Testnet (advanced)
  • Creating Wallets

SDK and Tools

  • SDKs and Tools - Overview
  • REST API

    • REST API overview
    • api.elrond.com
    • Gateway overview
    • Addresses
    • Transactions
    • Network
    • Nodes
    • Blocks
    • Virtual Machine
    • Versions and Changelog
  • Proxy
  • Elasticsearch
  • erdpy

    • erdpy
    • Installing erdpy
    • Configuring erdpy
    • erdpy CLI
    • Deriving the Wallet PEM file
    • Sending bulk transactions
    • Writing and running erdpy scripts
    • Smart contract interactions

    erdjs

    • erdjs
    • Cookbook
    • Extending erdjs
    • Writing and testing interactions
    • Migration guides
    • Signing Providers for dApps
  • erdgo
  • erdcpp
  • erdjava
  • erdkotlin
  • erdwalletjs-cli

Integrators

  • Integrators - Overview
  • EGLD integration guide
  • ESDT tokens integration guide
  • Observing Squad
  • Accounts Management
  • Creating Transactions
  • Querying the Blockchain

Mandos Complex Values

We already covered representations of simple types here. This is enough for arguments of types like usize, BigUint or &[u8], but we need to also somehow specify complex types like custom structs or lists of items.

Concatenation

It is possible to concatenate multiple expressions using the pipe operator (|). The pipe operator takes precedence over everything, so it is not currently possible to concatenate and then apply a function to the whole result.

This is ideal for short lists or small structs.

Example
  • a Vec<u32> can be expressed as "u32:1|u32:2|u32:3".
  • a (BigUint, BigUint) tuple can be expressed as "biguint:1|biguint:2"
  • a SimpleStruct { a: u8, b: BoxedBytes } can be expressed as "u8:4|nested:str:value-b"

Please note that the pipe operator only takes care of the concatenation itself. You are responsible for making sure that nested encoding is used where appropriate.

Using JSON lists as values

Mandos allows using JSON lists to express longer values. This especially makes sense when the value being represented is itself a list in the smart contract.

Example
  • a Vec<u32> can also be expressed as ["u32:1", "u32:2", "u32:3"].
  • a (BigUint, BigUint) tuple can also be expressed as ["biguint:1", "biguint:2"]
  • a SimpleStruct { a: u8, b: BoxedBytes } can also be expressed as ["u8:4", "nested:str:value-b"], although in this case a JSON map might be more appropriate.

Make sure not to confuse values expressed as JSON lists with other elements of Mandos syntax.

Example
{
            "step": "scCall",
            "txId": "echo_managed_vec_of_managed_vec",
            "tx": {
                "from": "address:an_account",
                "to": "sc:basic-features",
                "value": "0",
                "function": "echo_managed_vec_of_managed_vec",
                "arguments": [
                    [
                        "u32:3",
                        ["u32:1", "u32:2", "u32:3"],
                        "u32:0",
                        "u32:2",
                        ["u32:5", "u32:6"]
                    ]
                ],
                "gasLimit": "50,000,000",
                "gasPrice": "0"
            }
        },

In the example above, there is in fact a single argument that we are passing to the endpoint. The outer brackets in "arguments": [ ... ] are Mandos syntax for the list of arguments. The brackets immediately nested signal a JSON list value. Notice how the list itself contains some more lists inside it. They all get concatenated in the end into a single value.

In this example the only argument is 0x0000000300000001000000020000000300000000000000020000000500000006.

tip

We mentioned above how the developer needs to take care of the serialization of the nested items. This is actually a good example of that. The endpoint echo_managed_vec_of_managed_vec takes a list of lists, so we need to serialize the lengths of the lists on the second level. Notice how the lengths are given as JSON strings and the contents as JSON lists; the first "u32:3" is the serialized length of the first item, which is ["u32:1", "u32:2", "u32:3"], and so forth.

Using JSON maps as values

JSON lists make sense for representing series of items, but for structs JSON maps are more expressive.

The rules are as follows:

  • Mandos will concatenate all JSON map values and leave the keys out.
  • The keys need to be in alphanumerical order, so we customarily prefix them with numbers. Map keys in JSON are fundamentally unordered and this is the easiest way to enforce a deterministic order for the values.
  • Map values can be either JSON strings, lists or other maps, all Mandos value rules apply the same way all the way down.
Example

This is an abridged section of the actual lottery contract in the examples.

{
    "step": "checkState",
    "accounts": {
        "sc:lottery": {
            "storage": {
                "str:lotteryInfo|nested:str:lottery_name": {
                    "0-token_identifier": "nested:str:LOTTERY-123456",
                    "1-ticket_price": "biguint:100",
                    "2-tickets-left": "u32:0",
                    "3-deadline": "u64:123,456",
                    "4-max_entries_per_user": "u32:1",
                    "5-prize_distribution": ["u32:2", "u8:75", "u8:25"],
                    "6-whitelist": ["u32:3", "address:acc1", "address:acc2", "address:acc3"],
                    "7-prize_pool": "biguint:500"
                }
            },
            "code": "file:../output/lottery-esdt.wasm"
        }
    }
}

The Rust struct this translates to is:

#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi)]
pub struct LotteryInfo<M: ManagedTypeApi> {
    pub token_identifier: TokenIdentifier<M>,
    pub ticket_price: BigUint<M>,
    pub tickets_left: u32,
    pub deadline: u64,
    pub max_entries_per_user: u32,
    pub prize_distribution: Vec<u8>,
    pub whitelist: Vec<Address>,
    pub prize_pool: BigUint<M>,
}

tip

Once again, note that all contained values are in nested encoding format:

  • the token identifier has its length automatically prepended by the nested: prefix,
  • big ints are given with the biguint: syntax, which prepends their byte length,
  • small ints are given in full length,
  • lists have their length explicitly encoded at the start, always on 4 bytes (as u32).

A note about enums

There are 2 types of enums that we use in Rust:

  • simple enums are simply encoded as u8
  • complex enums are encoded just like structures, with an added u8 discriminant at the beginning.

Discriminants are not explicit in the Rust code, they get generated automatically. Discriminats start from 0.

Example

This is an abridged section of a Multisig contract test.

{
    "step": "checkState",
    "accounts": {
        "sc:multisig": {
            "storage": {
                "str:action_data.item|u32:3": {
                    "1-discriminant": "0x06",
                    "2-amount": "u32:0",
                    "3-code": "nested:file:../test-contracts/adder.wasm",
                    "4-code_metadata": "0x0000",
                    "5-arguments": [
                        "u32:1",
                        "u32:2|1234"
                    ]
                },
                "+": ""
            },
            "code": "file:../output/multisig.wasm"
        },
        "+": ""
    }
}

In this example we have a SCDeploy:

#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi)]
pub enum Action<M: ManagedTypeApi> {
    Nothing,
    AddBoardMember(ManagedAddress<M>),
    AddProposer(ManagedAddress<M>),
    RemoveUser(ManagedAddress<M>),
    ChangeQuorum(usize),
    SendEgld {
        to: ManagedAddress<M>,
        amount: BigUint<M>,
        data: BoxedBytes,
    },
    SCDeploy {
        amount: BigUint<M>,
        code: ManagedBuffer<M>,
        code_metadata: CodeMetadata,
        arguments: Vec<BoxedBytes>,
    },
    SCCall {
        to: ManagedAddress<M>,
        egld_payment: BigUint<M>,
        endpoint_name: BoxedBytes,
        arguments: Vec<BoxedBytes>,
    },
}
← Mandos Simple ValuesEmbedding Mandos code in Go →
  • Concatenation
  • Using JSON lists as values
  • Using JSON maps as values
  • A note about enums
Made withby the Elrond team.
GithubChat
Main siteWalletExplorerBridgeDocsGrowthMaiarMaiar Exchange