Learn Move Language by Small Moves!
In this blog post, I am going to introduce you to the Move language, which is the main language for developing smart contracts for the Aptos and Sui blockchian platforms. If you are unfamiliar with Aptos or Sui I would suggest you have a look at my previous posts or my YouTube where I explain them more in detail!
This blog introduces you to some insights about the language and its structure and I also share some of my personal experiences in this regard as a blockchain developer and security expert. Finally, we will have a review of security vulnerabilities in this ecosystem. So, if you are thinking of starting to learn this language, this post can be useful for you.
A Brief History of Move
Move is inspired by Rust which means it shares similarities in its focus on memory safety and ownership concepts but offers a simpler syntax reminiscent of JavaScript. Originally developed for Facebook’s Libra project, and later rebranded as Diem, this language has been refined to address the unique challenges of previous blockchain development in Ethereum and Solana. Note that the main emphasis of Move is on the resource-oriented programming paradigm which can help to enhance security by preventing common vulnerabilities found in traditional smart contracts like Solidity smart contracts (for EVM-based chains). We will discuss that paradigm a bit later.
By integrating Move with Aptos’ infrastructure, the language has become a cornerstone for building scalable smart contracts on the Aptos blockchain. Consequently, developers aiming to create smart contracts for Aptos (and also Sui) must learn and utilize Move. from my perspective as a developer who deployed smart contracts in Move, this is also a challgnes since Move is not as widespread as languages like Rust so new developers need to first learn the language which takes time and actually the computer is also evolving which introduce extra challenges for the adaptation of the language!
Key Features of Move
Move offers the following main features:
- Resource-Oriented Programming: Move introduces an approach where digital assets are treated as first-class resources. This ensures that assets cannot be duplicated or accidentally lost, enhancing the overall security of smart contracts.
- Formal Verification: Move supports formal verification methods, allowing developers to mathematically prove the correctness of their smart contracts. This reduces the likelihood of bugs and vulnerabilities that could be exploited by malicious actors. However, formal verification can be complex and is often utilized by advanced developers and researchers and needs some level of mathematic understanding!
- Modularity and Reusability: Move encourages the creation of reusable modules and libraries, promoting code efficiency and maintainability. Developers can build complex functionalities by composing simpler, well-tested components.
Move in Action!
Now we know the history behind Move, it is time to take some action and develop some smart contracts with Move on Mac OS, follow the steps below to install the necessary tools, compile our code, and run tests effectively.
Installing Move
First thing first, before we can start writing Move programs, we need to install the Move CLI (Command Line Interface). The Move CLI provides tools for compiling, testing, and deploying Move modules and scripts. I use Mac OS so I explain that based on that!
Install Prerequisites
Install Rust: Move is built using Rust, so first install Rust by running the following command in your terminal:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
Clone and Build Move: Clone the Move repository and build the Move CLI:
git clone https://github.com/move-language/move.git
cd move
cargo build --release
export PATH="$PATH:$HOME/move/target/release"
source ~/.zshrc # or source ~/.bash_profile
Set Up Your Move Project
Initialize a New Project: Create and navigate to a new Move project directory:
move init heyMove
cd heyMove
Compile Move Code
Compile Modules: Within your project directory, compile your Move modules using:
move build
Now you should see a confirmation message indicating successful compilation.
Test Our Smart Contracts
Write and Run Tests: Create a test module (e.g., tests/HelloWorldTests.move
) and write your tests:
module example::HelloWorldTests {
use move_std::assert;
use example::HelloWorld;
public fun test_greeting() {
let greeting = HelloWorld::get_greeting();
assert::assert_eq(greeting, "Hello, Move!");
}
}
Run the tests with:
We use Move CLI to run the test like this:
move test
You should see an output confirming that all tests have passed successfully.
Execute Move Scripts
Create and Run a Script: Write a Move script (e.g., scripts/HelloWorldScript.move
):
script {
use example::HelloWorld;
fun main() {
let message = HelloWorld::get_greeting();
std::debug::print(&message);
}
}
Execute the script with the CLI:
move run scripts/HelloWorldScript.move
The output should display:
Hello, Move!
Utilize SDKs and Development Tools
Aptos CLI and SDKs: Now we can leverage the Aptos CLI for compiling, managing accounts, and deploying modules. Moreover, utilize Aptos SDKs available in JavaScript, TypeScript, Python, and Rust to interact with the Aptos blockchain.
Writing Smart Contracts in Move for Aptos
To help new developers get started, let’s explore a set of smart contracts in Move for the Aptos blockchain, but if you understand this you can also apply it for the Sui chain, the same syntax with some modifications but here we only focus on Aptos. We’ll begin with a simple contract and gradually move to more practical examples to demonstrate Move’s capabilities and structure.
HelloWorld Contract
module Example::HelloWorld {
public fun get_greeting(): vector<u8> {
b"Hello, Move!"
}
}
The HelloWorld contract actually is a simple contract to familiarize you with the basic structure and syntax of Move modules. This minimalistic contract defines a single function, get_greeting
, which returns a byte vector containing the message "Hello, Move!". Now lets have a look at another contract that is a bit more practical and we call it “AccessControl Contract”
module Example::AccessControl {
use std::signer;
use std::vector;
use Example::Roles;
struct Roles has store {
admin: address,
users: vector<address>,
}
public fun initialize_roles(account: &signer) {
let roles = Roles {
admin: signer::address_of(account),
users: vector::empty<address>(),
};
move_to(account, roles);
}
public fun add_user(sender: &signer, new_user: address) {
let roles = borrow_global_mut<Roles>(signer::address_of(sender));
vector::push_back(&mut roles.users, new_user);
}
public fun remove_user(sender: &signer, user: address) {
let roles = borrow_global_mut<Roles>(signer::address_of(sender));
let index = vector::find(&roles.users, user);
if (option::is_some(&index)) {
let idx = option::unwrap(&index);
vector::swap_remove(&mut roles.users, idx);
}
}
public fun is_user(account: address): bool {
let roles = borrow_global<Roles>(account);
vector::contains(&roles.users, account)
}
}
So here we wrote this AccessControl contract which is essential for managing roles and permissions within the Aptos blockchain. In this contract, we want to be sure that only authorized accounts can perform specific actions. This contract is crucial for maintaining security and proper governance in decentralized applications by controlling who can add or remove users and verifying user privileges.
The imports at the beginning of the contract play a vital role in its functionality:
use std::signer;
: Imports the signer module from Move's standard library, providing access to signer-related functionalities such as verifying the identity of transaction senders.use std::vector;
: Imports the vector module, enabling the use of dynamic arrays to manage lists of user addresses efficiently.use Example::Roles;
: Imports the customRoles
module defined within the same project, which structures the role data by storing the admin's address and a list of user addresses.
By adding these imports to our code, the contract can initialize roles, add or remove users, and check if an account is a registered user, thereby enforcing access restrictions and maintaining the integrity of the system.
Now lets create a DEX in Move.
Decentralized Exchange (DEX) in Move
Here we are going to create a DEX contract in Move, DEX means Decentralized Exchange which shows a practical application of Move by having the exchange of tokens between users without intermediaries. This DEC contract includes several modules, including signer
, coin
, vector
, and debug
from the standard library, to manage trading operations and provide feedback during execution. Here is the code:
module Example::DEX {
use std::signer;
use std::coin;
use std::vector;
use std::debug;
struct Order has store {
trader: address,
sell_token: address,
buy_token: address,
sell_amount: u64,
buy_amount: u64,
}
public fun create_order(
trader: &signer,
sell_token: address,
buy_token: address,
sell_amount: u64,
buy_amount: u64
) {
let order = Order {
trader: signer::address_of(trader),
sell_token,
buy_token,
sell_amount,
buy_amount,
};
move_to(trader, order);
}
public fun execute_order(executor: &signer, order_address: address) {
let order = borrow_global_mut<Order>(order_address);
coin::withdraw(executor, order.sell_amount);
coin::deposit(order.buy_token, order.sell_amount);
coin::withdraw(executor, order.buy_amount);
coin::deposit(order.trader, order.buy_amount);
move_from<Order>(order_address);
debug::print(b"Order executed successfully.");
}
public fun cancel_order(trader: &signer, order_address: address) {
let order = borrow_global<Order>(order_address);
assert!(order.trader == signer::address_of(trader), 1001);
move_from<Order>(order_address);
debug::print(b"Order canceled.");
}
}
Here in this DEX contract, we define an Order
struct that encapsulates the details of a trade, such as the trader's address, the tokens to be sold and bought, and the respective amounts. The key functions include:
create_order
: Allows a trader to create a new exchange order by specifying the tokens and amounts they wish to trade.execute_order
: Enables an executor to fulfill an existing order by transferring the specified token amounts between the trader and executor.cancel_order
: Allows the trader to cancel their own order, ensuring that only the creator can perform this action.
By using ownership and borrowing mechanisms in Move, our DEX contract ensures that orders are securely managed and executed, preventing unauthorized access and ensuring the integrity of trades. Additionally, the use of the debug
module gives us immediate feedback upon successful execution or cancellation of orders, enhancing transparency and trust within the decentralized exchange.
Security in Move and Tools for Security Checking
Ensuring the security of smart contracts is paramount in blockchain development, and the Move language is no exception. While Move introduces several features aimed at enhancing security, such as resource-oriented programming and formal verification, it is essential to be aware of potential vulnerabilities and leverage available tools to mitigate risks. In this section, we will explore common security issues in Move and the tools designed to help developers identify and address these vulnerabilities effectively.
Common Security Vulnerabilities in Move
Despite Move’s robust security features, smart contracts written in Move can still be susceptible to various security issues if not carefully implemented. Understanding these vulnerabilities is crucial for developing secure decentralized applications on the Aptos and Sui blockchains.
Tools for Security Checking in Move
To address these vulnerabilities, the Move ecosystem provides several tools and practices that developers can utilize to enhance the security of their smart contracts. These tools facilitate the detection, analysis, and mitigation of potential security issues during the development lifecycle. In the following table we can see the listing of available tools (at the time of wiring this blog ) for enhancing the security of Move smart contracts:
Example: Utilizing Move Prover for Security
Consider enhancing the AccessControl contract to include formal verification of role-based permissions. By defining invariants that ensure only the admin can add or remove users, developers can use Move Prover to validate these properties.
module Example::AccessControl {
use std::signer;
use std::vector;
use Example::Roles;
use move_prover::verifier;
struct Roles has store {
admin: address,
users: vector<address>,
}
// --> Only the admin can modify the users list
verifier verify_admin_access(sender: address) {
let roles = borrow_global<Roles>(sender);
roles.admin == sender
}
public fun initialize_roles(account: &signer) {
let roles = Roles {
admin: signer::address_of(account),
users: vector::empty<address>(),
};
move_to(account, roles);
}
public fun add_user(sender: &signer, new_user: address) {
verifier::verify_admin_access(signer::address_of(sender));
let roles = borrow_global_mut<Roles>(signer::address_of(sender));
vector::push_back(&mut roles.users, new_user);
}
public fun remove_user(sender: &signer, user: address) {
verifier::verify_admin_access(signer::address_of(sender));
let roles = borrow_global_mut<Roles>(signer::address_of(sender));
let index = vector::find(&roles.users, user);
if (option::is_some(&index)) {
let idx = option::unwrap(&index);
vector::swap_remove(&mut roles.users, idx);
}
}
public fun is_user(account: address): bool {
let roles = borrow_global<Roles>(account);
vector::contains(&roles.users, account)
}
}
Here the verify_admin_access
verifier defines an invariant that ensures only the admin can modify the users' list. Before adding or removing a user, the contract invokes this verifier to confirm that the sender holds admin privileges. This method mathematically guarantees that unauthorized accounts cannot perform sensitive actions, thereby significantly strengthening the contract's security posture.
Finally words…
This blog does not make you a Move master but I hope it helps to get a taste of this environment especially if you are a new developer and want to start your career around Move. You can use Move to write relatively smart contracts for both the Aptos and Sui blockchain ecosystems. From crafting simple contracts like HelloWorld to developing more complex code such as AccessControl and DEX, Move helps you with the tools to build secure and scalable DApps. While the learning curve may present challenges, especially for those new to resource-oriented programming, the strong security features and modularity that Move offers make the effort worthwhile.