Move Basics

KHADIJAHKHADIJAH
6 min read

Introduction

What is Move?

Move is a next-generation programming language designed for secure, sandboxed, and formally verified smart contract development. It is used across multiple blockchain platforms, including Aptos, and offers robust security features that make it particularly well-suited for managing and transferring assets on the blockchain. Move's design ensures that resources (assets) are managed safely, avoiding many common pitfalls found in other smart contract languages.

Why Move Over Solidity and Foundry?

  • Security: Move's resource-oriented programming model ensures that assets are managed securely, preventing unauthorized duplication or deletion. This model provides stronger guarantees about the correctness and safety of smart contracts compared to Solidity.

  • Formal Verification: Move allows for formal verification of smart contracts, providing mathematical guarantees about the behaviour of contracts.

  • Modularity: Move promotes modular and reusable code through its module system, making it easier to build and maintain complex smart contracts.

In contrast, Solidity, while widely used on Ethereum, is known for its complex security pitfalls and less formal guarantees. Foundry, although a powerful development framework for Solidity, inherits the limitations of the Solidity language itself.

Variables and Types

Move supports several basic types such as u8, u64, u128, bool, address, and more. Here's an example of declaring variables and types:

module MyModule {
    public fun variable_example(): u64 {
        let my_u8: u8 = 10;
        let my_u64: u64 = 1000;
        let my_bool: bool = true;

        // Returning a value
        my_u64
    }
}

Functions and Procedures

Functions in Move can return values or modify the state. They are defined using the fun keyword.

  • Pure Functions: These do not modify the blockchain state and only return values.
module MyModule {
    public fun add(a: u64, b: u64): u64 {
        a + b
    }
}
  • Procedures: These can modify the blockchain state.
module MyModule {
    public fun update_value(account: &signer, new_value: u64) {
        // Logic to update some state with new_value
    }
}

Modules and Scripts

Modules define reusable code and encapsulate state and behaviour. Scripts are one-off transactions that can call functions in modules.

  • Modules:
module MyModule {
    struct Counter has key {
        value: u64,
    }

    public fun create_counter(account: &signer) {
        let counter = Counter { value: 0 };
        move_to(account, counter);
    }

    public fun increment_counter(account: &signer) {
        let counter = borrow_global_mut<Counter>(signer::address_of(account));
        counter.value = counter.value + 1;
    }

    public fun get_counter(account: address): u64 {
        let counter = borrow_global<Counter>(account);
        counter.value
    }
}
  • Scripts:
script {
    use 0x1::MyModule;

    fun main(account: &signer) {
        MyModule::create_counter(account);
        MyModule::increment_counter(account);
        let count = MyModule::get_counter(signer::address_of(account));
        assert!(count == 1, 0);
    }
}

In the example above, the module MyModule defines a Counter resource and functions to create, increment, and retrieve the counter value. The script demonstrates how to use these module functions.

5. Move Resource Model

Move's unique resource model ensures that resources (data that cannot be copied or discarded) are handled safely.

Overview of Resources

Resources are data types that can only exist in one location at a time and are managed by the blockchain's ownership rules.

module ResourceExample {
    struct MyResource has key {
        value: u64,
    }

    public fun create_resource(account: &signer) {
        let resource = MyResource { value: 10 };
        move_to(account, resource);
    }

    public fun use_resource(account: &signer) {
        let resource = borrow_global_mut<MyResource>(signer::address_of(account));
        resource.value = resource.value + 10;
    }

    public fun destroy_resource(account: &signer) {
        let resource = move_from<MyResource>(signer::address_of(account));
        // Resource is now taken out of the account, handle accordingly
    }
}

The resource MyResource can be created, used, and destroyed, ensuring that its lifecycle is managed properly.

Let's demonstrate what we have learned by writing a simple token contract to demonstrate move syntax and semantics

Sure! Let's write a comprehensive example to showcase Move's syntax and semantics, including variables and types, functions and procedures, and modules and scripts.

Example: A Simple Token Contract

In this example, we'll create a simple token contract that includes functionality to mint tokens, transfer tokens, and check balances. We'll cover the basics of variables, types, functions, and modules.

Step 1: Define the Module

First, we'll define a module for our token contract. The module will include a struct to represent the token and functions to manage it.

module SimpleToken {
    use 0x1::Signer;

    // Define the Token struct
    struct Token has key {
        balance: u64,
    }

    // Mint new tokens to an account
    public fun mint(account: &signer, amount: u64) {
        let token = borrow_global_mut<Token>(Signer::address_of(account));
        token.balance = token.balance + amount;
    }

    // Transfer tokens from the sender to the recipient
    public fun transfer(sender: &signer, recipient: address, amount: u64) {
        let sender_token = borrow_global_mut<Token>(Signer::address_of(sender));
        let recipient_token = borrow_global_mut<Token>(recipient);

        assert!(sender_token.balance >= amount, 1);
        sender_token.balance = sender_token.balance - amount;
        recipient_token.balance = recipient_token.balance + amount;
    }

    // Get the balance of an account
    public fun get_balance(account: address): u64 {
        let token = borrow_global<Token>(account);
        token.balance
    }

    // Initialize the Token for a given account
    public fun initialize(account: &signer) {
        move_to(account, Token { balance: 0 });
    }
}

Step 2: Script to Interact with the Module

Next, we'll write a script to interact with our SimpleToken module. This script will initialize the token for an account, mint some tokens, and perform a transfer.

script {
    use 0x1::SimpleToken;
    use 0x1::Signer;

    fun main(account: &signer) {
        // Initialize the token for the account
        SimpleToken::initialize(account);

        // Mint 1000 tokens to the account
        SimpleToken::mint(account, 1000);

        // Check the balance
        let balance = SimpleToken::get_balance(Signer::address_of(account));
        assert!(balance == 1000, 0);

        // Transfer 500 tokens to another account
        let recipient = 0x2;
        SimpleToken::initialize(&Signer::create_signer_for_address(recipient));
        SimpleToken::transfer(account, recipient, 500);

        // Check the balances
        let sender_balance = SimpleToken::get_balance(Signer::address_of(account));
        let recipient_balance = SimpleToken::get_balance(recipient);
        assert!(sender_balance == 500, 0);
        assert!(recipient_balance == 500, 0);
    }
}

Explanation

  1. Module Definition:

    • module SimpleToken { ... }: Defines the SimpleToken module.

    • struct Token has key { balance: u64, }defines a struct Token with a balance field.

    • public fun mint(account: &signer, amount: u64) { ... }mints new tokens into an account.

    • public fun transfer(sender: &signer, recipient: address, amount: u64) { ... }transfers tokens from the sender to the recipient.

    • public fun get_balance(account: address): u64 { ... }gets the balance of an account.

    • public fun initialize(account: &signer) { ... }initializes the token for an account.

  2. Script Definition:

    • script { ... }defines a script to interact with the SimpleToken module.

    • use 0x1::SimpleToken;imports the SimpleToken module.

    • use 0x1::Signer;imports the Signer module.

    • fun main(account: &signer) { ... }: Main function of the script.

      • Initializes the token for the account.

      • addsmain 1000 tokens to the account.

      • Check the balance.

      • Transfers 500 tokens to another account.

      • Checks the balances of both accounts.

This example showcases the basic syntax and semantics of the Move language, including variables, types, functions, procedures, and modules. You can expand upon this example by adding more functionality and testing it on the Aptos blockchain.

For more information as well as testing and deployment, please visit the documentation.

0
Subscribe to my newsletter

Read articles from KHADIJAH directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

KHADIJAH
KHADIJAH

Software | Blockchain | Developer Advocate | Technical Writer | Open source Contributor.